Loading app/src/androidTest/java/app/fedilab/android/ExampleInstrumentedTest.java +3 −3 Original line number Diff line number Diff line package app.fedilab.android; import static org.junit.Assert.assertEquals; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * Loading app/src/fdroid/java/app/fedilab/android/activities/BasePeertubeActivity.java 0 → 100644 +207 −0 Original line number Diff line number Diff line package app.fedilab.android.activities; /* Copyright 2021 Thomas Schneider * * This file is a part of TubeLab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see <http://www.gnu.org/licenses>. */ import static app.fedilab.android.peertube.helper.Helper.CAST_ID; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.webkit.MimeTypeMap; import androidx.appcompat.app.AlertDialog; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.google.android.exoplayer2.SimpleExoPlayer; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.security.GeneralSecurityException; import app.fedilab.android.R; import app.fedilab.android.databinding.ActivityPeertubeBinding; import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.peertube.client.data.VideoData; import app.fedilab.android.peertube.helper.Helper; import su.litvak.chromecast.api.v2.ChromeCast; import su.litvak.chromecast.api.v2.MediaStatus; import su.litvak.chromecast.api.v2.Status; public class BasePeertubeActivity extends BaseBarActivity { protected ActivityPeertubeBinding binding; protected VideoData.Video peertube; protected SimpleExoPlayer player; protected String videoURL; protected String subtitlesStr; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityPeertubeBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); binding.minController.castPlay.setOnClickListener(v -> { binding.minController.castLoader.setVisibility(View.VISIBLE); if (PeertubeBaseMainActivity.chromeCast != null) { new Thread(() -> { try { int icon = -1; if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { PeertubeBaseMainActivity.chromeCast.pause(); icon = R.drawable.ic_baseline_play_arrow_32; } else if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { PeertubeBaseMainActivity.chromeCast.play(); icon = R.drawable.ic_baseline_pause_32; } if (icon != -1) { Handler mainHandler = new Handler(Looper.getMainLooper()); int finalIcon = icon; Runnable myRunnable = () -> binding.minController.castPlay.setImageResource(finalIcon); mainHandler.post(myRunnable); } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE); mainHandler.post(myRunnable); } catch (IOException e) { e.printStackTrace(); } }).start(); } }); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_cast) { if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) { String[] chromecast_choice = new String[PeertubeBaseMainActivity.chromeCasts.size()]; AlertDialog.Builder alt_bld = new AlertDialog.Builder(this); alt_bld.setTitle(R.string.chromecast_choice); int i = 0; for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) { chromecast_choice[i] = cc.getTitle(); i++; } i = 0; for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) { if (PeertubeBaseMainActivity.chromecastActivated && cc.isConnected()) { break; } i++; } alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> { PeertubeBaseMainActivity.chromeCast = PeertubeBaseMainActivity.chromeCasts.get(position); new Thread(() -> { if (PeertubeBaseMainActivity.chromeCast != null) { Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); Bundle b = new Bundle(); if (PeertubeBaseMainActivity.chromecastActivated) { b.putInt("displayed", 0); intentBC.putExtras(b); LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { binding.doubleTapPlayerView.setVisibility(View.VISIBLE); binding.minController.castMiniController.setVisibility(View.GONE); }; mainHandler.post(myRunnable); } else { b.putInt("displayed", 1); b.putParcelable("castedTube", peertube); intentBC.putExtras(b); LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC); try { Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { invalidateOptionsMenu(); binding.minController.castLoader.setVisibility(View.VISIBLE); player.setPlayWhenReady(false); binding.doubleTapPlayerView.setVisibility(View.GONE); binding.minController.castMiniController.setVisibility(View.VISIBLE); dialog.dismiss(); if (videoURL != null) { if (player != null && player.getCurrentPosition() > 0) { videoURL += "?start=" + (player.getCurrentPosition() / 1000); } } }; mainHandler.post(myRunnable); if (!PeertubeBaseMainActivity.chromeCast.isConnected()) { PeertubeBaseMainActivity.chromeCast.connect(); } myRunnable = this::invalidateOptionsMenu; mainHandler.post(myRunnable); Status status = PeertubeBaseMainActivity.chromeCast.getStatus(); if (PeertubeBaseMainActivity.chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) { PeertubeBaseMainActivity.chromeCast.launchApp(CAST_ID); } if (videoURL != null) { String mime = MimeTypeMap.getFileExtensionFromUrl(videoURL); PeertubeBaseMainActivity.chromeCast.setRequestTimeout(60000); PeertubeBaseMainActivity.chromeCast.load(peertube.getTitle(), null, videoURL, mime); PeertubeBaseMainActivity.chromeCast.play(); binding.minController.castPlay.setImageResource(R.drawable.ic_baseline_pause_32); } myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE); mainHandler.post(myRunnable); } 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); } @Override public boolean onCreateOptionsMenu(@NotNull Menu menu) { getMenuInflater().inflate(R.menu.video_menu, menu); MenuItem castItem = menu.findItem(R.id.action_cast); if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) { castItem.setVisible(true); if (PeertubeBaseMainActivity.chromeCast != null && PeertubeBaseMainActivity.chromeCast.isConnected()) { castItem.setIcon(R.drawable.ic_baseline_cast_connected_24); } else { castItem.setIcon(R.drawable.ic_baseline_cast_24); } } return true; } } app/src/fdroid/java/app/fedilab/android/activities/PeertubeBaseMainActivity.java 0 → 100644 +262 −0 Original line number Diff line number Diff line package app.fedilab.android.activities; /* Copyright 2021 Thomas Schneider * * This file is a part of TubeLab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see <http://www.gnu.org/licenses>. */ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import app.fedilab.android.R; import app.fedilab.android.databinding.ActivityMainPeertubeBinding; import app.fedilab.android.peertube.client.data.VideoData; import app.fedilab.android.peertube.helper.Helper; 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.MediaStatus; public abstract class PeertubeBaseMainActivity extends AppCompatActivity implements ChromeCastsListener { public static List<ChromeCast> chromeCasts; public static ChromeCast chromeCast; public static boolean chromecastActivated = false; protected ActivityMainPeertubeBinding binding; private BroadcastReceiver manage_chromecast; private VideoData.Video castedTube; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); ChromeCastsListener chromeCastsListener = this; ChromeCasts.registerListener(chromeCastsListener); binding.castClose.setOnClickListener(v -> { Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); Bundle b = new Bundle(); b.putInt("displayed", 0); intentBC.putExtras(b); LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).sendBroadcast(intentBC); }); binding.castTogglePlay.setOnClickListener(v -> { if (chromeCast != null) { new Thread(() -> { try { Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE); mainHandler.post(myRunnable); int icon = -1; if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { chromeCast.pause(); icon = R.drawable.ic_baseline_play_arrow_32; } else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { chromeCast.play(); icon = R.drawable.ic_baseline_pause_32; } if (icon != -1) { int finalIcon = icon; myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon); mainHandler.post(myRunnable); } myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE); mainHandler.post(myRunnable); } catch (IOException e) { e.printStackTrace(); } }).start(); } }); manage_chromecast = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Bundle b = intent.getExtras(); assert b != null; int state = b.getInt("state_asked", -1); int displayed = b.getInt("displayed", -1); castedTube = b.getParcelable("castedTube"); if (state == 1) { discoverCast(); } else if (state == 0) { new Thread(() -> { try { if (chromeCast != null) { chromeCast.stopApp(); chromeCast.disconnect(); } } catch (IOException e) { e.printStackTrace(); } }).start(); } if (displayed == 1) { chromecastActivated = true; if (castedTube != null) { binding.castInfo.setVisibility(View.VISIBLE); Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), binding.castView); binding.castTitle.setText(castedTube.getTitle()); binding.castDescription.setText(castedTube.getDescription()); } } else if (displayed == 0) { chromecastActivated = false; binding.castInfo.setVisibility(View.GONE); new Thread(() -> { try { if (chromeCast != null) { chromeCast.stopApp(); } } catch (IOException e) { e.printStackTrace(); } }).start(); } } }; LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS)); } @Override public void newChromeCastDiscovered(ChromeCast chromeCast) { if (chromeCasts == null) { chromeCasts = new ArrayList<>(); chromeCasts.add(chromeCast); } else { boolean canBeAdded = true; for (ChromeCast cast : chromeCasts) { if (cast.getName().compareTo(chromeCast.getName()) == 0) { canBeAdded = false; break; } } if (canBeAdded) { chromeCasts.add(chromeCast); } } try { if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) { if (binding.castInfo.getVisibility() == View.GONE) { binding.castInfo.setVisibility(View.VISIBLE); } } } catch (IOException e) { e.printStackTrace(); } } @Override public void chromeCastRemoved(ChromeCast chromeCast) { } @Override public void onDestroy() { super.onDestroy(); ChromeCasts.unregisterListener(this); if (manage_chromecast != null) { LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).unregisterReceiver(manage_chromecast); new Thread(() -> { if (chromeCasts != null && chromeCasts.size() > 0) { for (ChromeCast cast : chromeCasts) { try { cast.stopApp(); cast.disconnect(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } if (chromeCasts != null) { chromeCasts = null; } if (chromeCast != null) { chromeCast = null; } } //Method for discovering cast devices public void discoverCast() { new Thread(() -> { if (chromeCasts != null) { for (ChromeCast cast : chromeCasts) { try { cast.disconnect(); } catch (IOException e) { e.printStackTrace(); } } chromeCasts = null; } chromeCasts = new ArrayList<>(); try { List<NetworkInterface> interfaces; interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface ni : interfaces) { if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) { Enumeration<InetAddress> inetAddressEnumeration = ni.getInetAddresses(); while (inetAddressEnumeration.hasMoreElements()) { InetAddress inetAddress = inetAddressEnumeration.nextElement(); ChromeCasts.restartDiscovery(inetAddress); int tryFind = 0; while (ChromeCasts.get().isEmpty() && tryFind < 5) { try { //noinspection BusyWait Thread.sleep(1000); tryFind++; } catch (InterruptedException ignored) { } } } } } ChromeCasts.stopDiscovery(); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = this::invalidateOptionsMenu; mainHandler.post(myRunnable); } catch (IOException e) { e.printStackTrace(); } }).start(); } } app/src/fdroid/res/menu/video_menu.xml 0 → 100644 +10 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_cast" android:icon="@drawable/ic_baseline_cast_24" android:title="@string/cast" android:visible="false" app:showAsAction="always" /> </menu> app/src/main/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -28,9 +28,9 @@ android:icon="@mipmap/ic_launcher_bubbles" android:label="@string/app_name" android:largeHeap="true" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_bubbles_round" android:supportsRtl="true" android:requestLegacyExternalStorage="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true" tools:replace="android:allowBackup"> Loading Loading
app/src/androidTest/java/app/fedilab/android/ExampleInstrumentedTest.java +3 −3 Original line number Diff line number Diff line package app.fedilab.android; import static org.junit.Assert.assertEquals; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * Loading
app/src/fdroid/java/app/fedilab/android/activities/BasePeertubeActivity.java 0 → 100644 +207 −0 Original line number Diff line number Diff line package app.fedilab.android.activities; /* Copyright 2021 Thomas Schneider * * This file is a part of TubeLab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see <http://www.gnu.org/licenses>. */ import static app.fedilab.android.peertube.helper.Helper.CAST_ID; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.webkit.MimeTypeMap; import androidx.appcompat.app.AlertDialog; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.google.android.exoplayer2.SimpleExoPlayer; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.security.GeneralSecurityException; import app.fedilab.android.R; import app.fedilab.android.databinding.ActivityPeertubeBinding; import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.peertube.client.data.VideoData; import app.fedilab.android.peertube.helper.Helper; import su.litvak.chromecast.api.v2.ChromeCast; import su.litvak.chromecast.api.v2.MediaStatus; import su.litvak.chromecast.api.v2.Status; public class BasePeertubeActivity extends BaseBarActivity { protected ActivityPeertubeBinding binding; protected VideoData.Video peertube; protected SimpleExoPlayer player; protected String videoURL; protected String subtitlesStr; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityPeertubeBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); binding.minController.castPlay.setOnClickListener(v -> { binding.minController.castLoader.setVisibility(View.VISIBLE); if (PeertubeBaseMainActivity.chromeCast != null) { new Thread(() -> { try { int icon = -1; if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { PeertubeBaseMainActivity.chromeCast.pause(); icon = R.drawable.ic_baseline_play_arrow_32; } else if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { PeertubeBaseMainActivity.chromeCast.play(); icon = R.drawable.ic_baseline_pause_32; } if (icon != -1) { Handler mainHandler = new Handler(Looper.getMainLooper()); int finalIcon = icon; Runnable myRunnable = () -> binding.minController.castPlay.setImageResource(finalIcon); mainHandler.post(myRunnable); } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE); mainHandler.post(myRunnable); } catch (IOException e) { e.printStackTrace(); } }).start(); } }); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_cast) { if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) { String[] chromecast_choice = new String[PeertubeBaseMainActivity.chromeCasts.size()]; AlertDialog.Builder alt_bld = new AlertDialog.Builder(this); alt_bld.setTitle(R.string.chromecast_choice); int i = 0; for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) { chromecast_choice[i] = cc.getTitle(); i++; } i = 0; for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) { if (PeertubeBaseMainActivity.chromecastActivated && cc.isConnected()) { break; } i++; } alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> { PeertubeBaseMainActivity.chromeCast = PeertubeBaseMainActivity.chromeCasts.get(position); new Thread(() -> { if (PeertubeBaseMainActivity.chromeCast != null) { Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); Bundle b = new Bundle(); if (PeertubeBaseMainActivity.chromecastActivated) { b.putInt("displayed", 0); intentBC.putExtras(b); LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { binding.doubleTapPlayerView.setVisibility(View.VISIBLE); binding.minController.castMiniController.setVisibility(View.GONE); }; mainHandler.post(myRunnable); } else { b.putInt("displayed", 1); b.putParcelable("castedTube", peertube); intentBC.putExtras(b); LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC); try { Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { invalidateOptionsMenu(); binding.minController.castLoader.setVisibility(View.VISIBLE); player.setPlayWhenReady(false); binding.doubleTapPlayerView.setVisibility(View.GONE); binding.minController.castMiniController.setVisibility(View.VISIBLE); dialog.dismiss(); if (videoURL != null) { if (player != null && player.getCurrentPosition() > 0) { videoURL += "?start=" + (player.getCurrentPosition() / 1000); } } }; mainHandler.post(myRunnable); if (!PeertubeBaseMainActivity.chromeCast.isConnected()) { PeertubeBaseMainActivity.chromeCast.connect(); } myRunnable = this::invalidateOptionsMenu; mainHandler.post(myRunnable); Status status = PeertubeBaseMainActivity.chromeCast.getStatus(); if (PeertubeBaseMainActivity.chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) { PeertubeBaseMainActivity.chromeCast.launchApp(CAST_ID); } if (videoURL != null) { String mime = MimeTypeMap.getFileExtensionFromUrl(videoURL); PeertubeBaseMainActivity.chromeCast.setRequestTimeout(60000); PeertubeBaseMainActivity.chromeCast.load(peertube.getTitle(), null, videoURL, mime); PeertubeBaseMainActivity.chromeCast.play(); binding.minController.castPlay.setImageResource(R.drawable.ic_baseline_pause_32); } myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE); mainHandler.post(myRunnable); } 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); } @Override public boolean onCreateOptionsMenu(@NotNull Menu menu) { getMenuInflater().inflate(R.menu.video_menu, menu); MenuItem castItem = menu.findItem(R.id.action_cast); if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) { castItem.setVisible(true); if (PeertubeBaseMainActivity.chromeCast != null && PeertubeBaseMainActivity.chromeCast.isConnected()) { castItem.setIcon(R.drawable.ic_baseline_cast_connected_24); } else { castItem.setIcon(R.drawable.ic_baseline_cast_24); } } return true; } }
app/src/fdroid/java/app/fedilab/android/activities/PeertubeBaseMainActivity.java 0 → 100644 +262 −0 Original line number Diff line number Diff line package app.fedilab.android.activities; /* Copyright 2021 Thomas Schneider * * This file is a part of TubeLab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see <http://www.gnu.org/licenses>. */ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import app.fedilab.android.R; import app.fedilab.android.databinding.ActivityMainPeertubeBinding; import app.fedilab.android.peertube.client.data.VideoData; import app.fedilab.android.peertube.helper.Helper; 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.MediaStatus; public abstract class PeertubeBaseMainActivity extends AppCompatActivity implements ChromeCastsListener { public static List<ChromeCast> chromeCasts; public static ChromeCast chromeCast; public static boolean chromecastActivated = false; protected ActivityMainPeertubeBinding binding; private BroadcastReceiver manage_chromecast; private VideoData.Video castedTube; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); ChromeCastsListener chromeCastsListener = this; ChromeCasts.registerListener(chromeCastsListener); binding.castClose.setOnClickListener(v -> { Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); Bundle b = new Bundle(); b.putInt("displayed", 0); intentBC.putExtras(b); LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).sendBroadcast(intentBC); }); binding.castTogglePlay.setOnClickListener(v -> { if (chromeCast != null) { new Thread(() -> { try { Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE); mainHandler.post(myRunnable); int icon = -1; if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { chromeCast.pause(); icon = R.drawable.ic_baseline_play_arrow_32; } else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { chromeCast.play(); icon = R.drawable.ic_baseline_pause_32; } if (icon != -1) { int finalIcon = icon; myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon); mainHandler.post(myRunnable); } myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE); mainHandler.post(myRunnable); } catch (IOException e) { e.printStackTrace(); } }).start(); } }); manage_chromecast = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Bundle b = intent.getExtras(); assert b != null; int state = b.getInt("state_asked", -1); int displayed = b.getInt("displayed", -1); castedTube = b.getParcelable("castedTube"); if (state == 1) { discoverCast(); } else if (state == 0) { new Thread(() -> { try { if (chromeCast != null) { chromeCast.stopApp(); chromeCast.disconnect(); } } catch (IOException e) { e.printStackTrace(); } }).start(); } if (displayed == 1) { chromecastActivated = true; if (castedTube != null) { binding.castInfo.setVisibility(View.VISIBLE); Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), binding.castView); binding.castTitle.setText(castedTube.getTitle()); binding.castDescription.setText(castedTube.getDescription()); } } else if (displayed == 0) { chromecastActivated = false; binding.castInfo.setVisibility(View.GONE); new Thread(() -> { try { if (chromeCast != null) { chromeCast.stopApp(); } } catch (IOException e) { e.printStackTrace(); } }).start(); } } }; LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS)); } @Override public void newChromeCastDiscovered(ChromeCast chromeCast) { if (chromeCasts == null) { chromeCasts = new ArrayList<>(); chromeCasts.add(chromeCast); } else { boolean canBeAdded = true; for (ChromeCast cast : chromeCasts) { if (cast.getName().compareTo(chromeCast.getName()) == 0) { canBeAdded = false; break; } } if (canBeAdded) { chromeCasts.add(chromeCast); } } try { if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) { if (binding.castInfo.getVisibility() == View.GONE) { binding.castInfo.setVisibility(View.VISIBLE); } } } catch (IOException e) { e.printStackTrace(); } } @Override public void chromeCastRemoved(ChromeCast chromeCast) { } @Override public void onDestroy() { super.onDestroy(); ChromeCasts.unregisterListener(this); if (manage_chromecast != null) { LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).unregisterReceiver(manage_chromecast); new Thread(() -> { if (chromeCasts != null && chromeCasts.size() > 0) { for (ChromeCast cast : chromeCasts) { try { cast.stopApp(); cast.disconnect(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } if (chromeCasts != null) { chromeCasts = null; } if (chromeCast != null) { chromeCast = null; } } //Method for discovering cast devices public void discoverCast() { new Thread(() -> { if (chromeCasts != null) { for (ChromeCast cast : chromeCasts) { try { cast.disconnect(); } catch (IOException e) { e.printStackTrace(); } } chromeCasts = null; } chromeCasts = new ArrayList<>(); try { List<NetworkInterface> interfaces; interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface ni : interfaces) { if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) { Enumeration<InetAddress> inetAddressEnumeration = ni.getInetAddresses(); while (inetAddressEnumeration.hasMoreElements()) { InetAddress inetAddress = inetAddressEnumeration.nextElement(); ChromeCasts.restartDiscovery(inetAddress); int tryFind = 0; while (ChromeCasts.get().isEmpty() && tryFind < 5) { try { //noinspection BusyWait Thread.sleep(1000); tryFind++; } catch (InterruptedException ignored) { } } } } } ChromeCasts.stopDiscovery(); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = this::invalidateOptionsMenu; mainHandler.post(myRunnable); } catch (IOException e) { e.printStackTrace(); } }).start(); } }
app/src/fdroid/res/menu/video_menu.xml 0 → 100644 +10 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_cast" android:icon="@drawable/ic_baseline_cast_24" android:title="@string/cast" android:visible="false" app:showAsAction="always" /> </menu>
app/src/main/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -28,9 +28,9 @@ android:icon="@mipmap/ic_launcher_bubbles" android:label="@string/app_name" android:largeHeap="true" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_bubbles_round" android:supportsRtl="true" android:requestLegacyExternalStorage="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true" tools:replace="android:allowBackup"> Loading