diff --git a/app/build.gradle b/app/build.gradle
index 58b5a84..d22555b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -126,7 +126,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
- testImplementation 'junit:junit:4.13'
+ testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
@@ -155,5 +155,7 @@ dependencies {
implementation "androidx.work:work-runtime:2.4.0"
implementation "androidx.work:work-runtime-ktx:2.4.0"
implementation 'jp.wasabeef:glide-transformations:4.0.0'
+ implementation 'su.litvak.chromecast:api-v2:0.11.3'
+ implementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
}
\ No newline at end of file
diff --git a/app/src/acad/res/values/strings.xml b/app/src/acad/res/values/strings.xml
index 671d716..5ac6096 100644
--- a/app/src/acad/res/values/strings.xml
+++ b/app/src/acad/res/values/strings.xml
@@ -17,7 +17,8 @@
set_video_sensitive_choice
Vidéos dans une liste
Change la mise en page pour afficher les vidéos dans une liste
-
+ ChromeCast
+ Choix de la ChromeCast
Réessayer
Échec de rafraîchissement du jeton d\'accès
Vous pouvez réessayer de le rafraîchir ou simplement déconnecter le compte
diff --git a/app/src/bittube/res/values/strings.xml b/app/src/bittube/res/values/strings.xml
index 35c10c7..55439ff 100644
--- a/app/src/bittube/res/values/strings.xml
+++ b/app/src/bittube/res/values/strings.xml
@@ -19,7 +19,8 @@
No instances !
Show more
Show less
-
+ ChromeCast
+ ChromeCast choice
Screen lock
Keep playing videos when the screen is locked
diff --git a/app/src/full/res/values/strings.xml b/app/src/full/res/values/strings.xml
index 88bd203..72d782a 100644
--- a/app/src/full/res/values/strings.xml
+++ b/app/src/full/res/values/strings.xml
@@ -19,6 +19,8 @@
No instances !
Show more
Show less
+ ChromeCast
+ ChromeCast choice
Screen lock
Keep playing videos when the screen is locked
diff --git a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java
index 1346364..8eccdfb 100644
--- a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java
+++ b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java
@@ -24,6 +24,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -47,6 +48,11 @@ import com.kobakei.ratethisapp.RateThisApp;
import org.jetbrains.annotations.NotNull;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Collections;
+import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
@@ -77,6 +83,7 @@ import app.fedilab.fedilabtube.sqlite.Sqlite;
import app.fedilab.fedilabtube.sqlite.StoredInstanceDAO;
import app.fedilab.fedilabtube.viewmodel.TimelineVM;
import es.dmoral.toasty.Toasty;
+import su.litvak.chromecast.api.v2.ChromeCasts;
import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.NORMAL;
import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.SURFING;
@@ -198,6 +205,36 @@ public class MainActivity extends AppCompatActivity {
View view = binding.getRoot();
setContentView(view);
+ new Thread(() -> {
+ try {
+ List interfaces;
+ interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+ for (NetworkInterface ni : interfaces) {
+ if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) {
+ Enumeration inetAddressEnumeration = ni.getInetAddresses();
+ while (inetAddressEnumeration.hasMoreElements()) {
+ InetAddress inetAddress = inetAddressEnumeration.nextElement();
+ ChromeCasts.restartDiscovery(inetAddress);
+ PeertubeActivity.chromeCasts = ChromeCasts.get();
+ int tryFind = 0;
+ while (PeertubeActivity.chromeCasts.isEmpty() && tryFind < 5) {
+ try {
+ Thread.sleep(1000);
+ PeertubeActivity.chromeCasts = ChromeCasts.get();
+ tryFind++;
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ }
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = this::invalidateOptionsMenu;
+ mainHandler.post(myRunnable);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }).start();
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@@ -722,7 +759,7 @@ public class MainActivity extends AppCompatActivity {
return locaFragment;
}
}
- return null;
+ return overviewFragment;
}
@Override
diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java
index f3006c1..530bee0 100644
--- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java
+++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java
@@ -44,6 +44,7 @@ import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
@@ -103,6 +104,8 @@ import com.google.android.exoplayer2.video.VideoListener;
import org.jetbrains.annotations.NotNull;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.HashMap;
@@ -133,6 +136,7 @@ import app.fedilab.fedilabtube.drawer.CommentListAdapter;
import app.fedilab.fedilabtube.drawer.MenuAdapter;
import app.fedilab.fedilabtube.drawer.MenuItemAdapter;
import app.fedilab.fedilabtube.helper.CacheDataSourceFactory;
+import app.fedilab.fedilabtube.helper.DashCastRequest;
import app.fedilab.fedilabtube.helper.Helper;
import app.fedilab.fedilabtube.helper.HelperInstance;
import app.fedilab.fedilabtube.sqlite.AccountDAO;
@@ -147,6 +151,11 @@ import app.fedilab.fedilabtube.webview.CustomWebview;
import app.fedilab.fedilabtube.webview.MastalabWebChromeClient;
import app.fedilab.fedilabtube.webview.MastalabWebViewClient;
import es.dmoral.toasty.Toasty;
+import su.litvak.chromecast.api.v2.Application;
+import su.litvak.chromecast.api.v2.ChromeCast;
+import su.litvak.chromecast.api.v2.ChromeCasts;
+import su.litvak.chromecast.api.v2.ChromeCastsListener;
+import su.litvak.chromecast.api.v2.Status;
import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.ADD_COMMENT;
import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.RATEVIDEO;
@@ -160,7 +169,7 @@ import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation;
import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO;
-public class PeertubeActivity extends AppCompatActivity implements CommentListAdapter.AllCommentRemoved, Player.EventListener, VideoListener, TorrentListener, MenuAdapter.ItemClicked, MenuItemAdapter.ItemAction {
+public class PeertubeActivity extends AppCompatActivity implements CommentListAdapter.AllCommentRemoved, Player.EventListener, VideoListener, TorrentListener, MenuAdapter.ItemClicked, MenuItemAdapter.ItemAction, ChromeCastsListener {
public static String video_id;
public static List playedVideos = new ArrayList<>();
@@ -194,6 +203,9 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
private String currentCaption;
private boolean isRemote;
private boolean willPlayFromIntent;
+ public static List chromeCasts;
+ private ChromeCast chromeCast;
+ private String chromeCastVideoURL;
public static void hideKeyboard(Activity activity) {
if (activity != null && activity.getWindow() != null) {
@@ -240,6 +252,14 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
public void onStreamStopped() {
}
+ @Override
+ public void newChromeCastDiscovered(ChromeCast chromeCast) {
+ }
+
+ @Override
+ public void chromeCastRemoved(ChromeCast chromeCast) {
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -263,6 +283,9 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
.removeFilesAfterStop(true)
.build();
+
+ ChromeCastsListener chromeCastsListener = this;
+ ChromeCasts.registerListener(chromeCastsListener);
fullScreenMode = false;
torrentStream = TorrentStream.init(torrentOptions);
torrentStream.addListener(PeertubeActivity.this);
@@ -661,6 +684,20 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
}).start();
}
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.video_menu, menu);
+ MenuItem castItem = menu.findItem(R.id.action_cast);
+ if (chromeCasts != null && chromeCasts.size() > 0) {
+ castItem.setVisible(true);
+ if (chromeCast != null && chromeCast.isConnected()) {
+ castItem.setIcon(R.drawable.ic_baseline_cast_connected_24);
+ } else {
+ castItem.setIcon(R.drawable.ic_baseline_cast_24);
+ }
+ }
+ return true;
+ }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@@ -670,6 +707,69 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
}
finish();
return true;
+ } else if (item.getItemId() == R.id.action_cast) {
+ if (chromeCasts != null && chromeCasts.size() > 0) {
+ String[] chromecast_choice = new String[chromeCasts.size()];
+ AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
+ alt_bld.setTitle(R.string.chromecast_choice);
+ int i = 0;
+ for (ChromeCast cc : chromeCasts) {
+ chromecast_choice[i] = cc.getTitle();
+ i++;
+ }
+ alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> {
+ chromeCast = chromeCasts.get(position);
+ new Thread(() -> {
+ if (chromeCast != null) {
+ if (chromeCast.isConnected()) {
+ try {
+ chromeCast.disconnect();
+ chromeCast = null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ chromeCast.connect();
+
+
+ Status status = chromeCast.getStatus();
+
+ String app_id = status.getRunningApp().id;
+ if (chromeCast.isAppAvailable(app_id) && !status.isAppRunning(app_id)) {
+ Application app = chromeCast.launchApp(app_id);
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ invalidateOptionsMenu();
+ dialog.dismiss();
+ };
+ mainHandler.post(myRunnable);
+ if (chromeCastVideoURL != null) {
+ chromeCast.load(peertube.getTitle(), "https://" + HelperInstance.getLiveInstance(PeertubeActivity.this) + peertube.getThumbnailPath(), chromeCastVideoURL, null);
+ // chromeCast.send("urn:x-cast:es.offd.dashcast", new DashCastRequest(chromeCastVideoURL, true, false, 0));
+ }
+
+ } catch (IOException | GeneralSecurityException e) {
+ e.printStackTrace();
+ }
+ }
+
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ invalidateOptionsMenu();
+ dialog.dismiss();
+ };
+ mainHandler.post(myRunnable);
+
+ }
+ }).start();
+
+ });
+ alt_bld.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
+ AlertDialog alert = alt_bld.create();
+ alert.show();
+ }
}
return super.onOptionsItemSelected(item);
}
@@ -1182,6 +1282,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
private void startStream(String videoURL, String streamingPlaylistsURLS, boolean autoPlay, long position, Uri subtitles, String lang, boolean promptNSFW) {
+ chromeCastVideoURL = videoURL;
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
String nsfwAction = sharedpreferences.getString(getString(R.string.set_video_sensitive_choice), Helper.BLUR);
if (promptNSFW && peertube != null && peertube.isNsfw() && (nsfwAction.compareTo(Helper.BLUR) == 0 || nsfwAction.compareTo(Helper.DO_NOT_LIST) == 0)) {
@@ -1205,6 +1306,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
stream(videoURL, streamingPlaylistsURLS, autoPlay, position, subtitles, lang);
}
+
}
@Override
@@ -1247,6 +1349,14 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd
if (torrentStream != null && torrentStream.isStreaming()) {
torrentStream.stopStream();
}
+ ChromeCasts.unregisterListener(this);
+ if (chromeCast != null) {
+ try {
+ chromeCast.disconnect();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
unregisterReceiver();
}
diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/DashCastRequest.java b/app/src/main/java/app/fedilab/fedilabtube/helper/DashCastRequest.java
new file mode 100644
index 0000000..9a325ca
--- /dev/null
+++ b/app/src/main/java/app/fedilab/fedilabtube/helper/DashCastRequest.java
@@ -0,0 +1,38 @@
+package app.fedilab.fedilabtube.helper;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import su.litvak.chromecast.api.v2.Request;
+
+public class DashCastRequest implements Request {
+ @JsonProperty
+ final String url;
+ @JsonProperty
+ final boolean force;
+ @JsonProperty
+ final boolean reload;
+ @JsonProperty("reload_time")
+ final int reloadTime;
+
+ private Long requestId;
+
+ public DashCastRequest(String url,
+ boolean force,
+ boolean reload,
+ int reloadTime) {
+ this.url = url;
+ this.force = force;
+ this.reload = reload;
+ this.reloadTime = reloadTime;
+ }
+
+ @Override
+ public Long getRequestId() {
+ return requestId;
+ }
+
+ @Override
+ public void setRequestId(Long requestId) {
+ this.requestId = requestId;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_cast_24.xml b/app/src/main/res/drawable/ic_baseline_cast_24.xml
new file mode 100644
index 0000000..beb2f68
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_cast_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_cast_connected_24.xml b/app/src/main/res/drawable/ic_baseline_cast_connected_24.xml
new file mode 100644
index 0000000..7f2eab7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_cast_connected_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/menu/video_menu.xml b/app/src/main/res/menu/video_menu.xml
new file mode 100644
index 0000000..88d4e36
--- /dev/null
+++ b/app/src/main/res/menu/video_menu.xml
@@ -0,0 +1,10 @@
+
+