diff --git a/app/build.gradle b/app/build.gradle index 70af9de..6fc471d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,6 +53,9 @@ android { buildConfigField "boolean", "sepia_search", "false" buildConfigField "boolean", "instance_switcher", "true" buildConfigField "boolean", "allow_remote_connections", "false" + buildConfigField "boolean", "google_cast_lib", "false" + buildConfigField "int", "cast_enabled", "0" + buildConfigField "int", "default_theme", "2" } google_peertube_apps_educ { applicationId "app.fedilab.fedilabtube" @@ -65,6 +68,9 @@ android { buildConfigField "boolean", "sepia_search", "false" buildConfigField "boolean", "instance_switcher", "true" buildConfigField "boolean", "allow_remote_connections", "false" + buildConfigField "boolean", "google_cast_lib", "false" + buildConfigField "int", "cast_enabled", "0" + buildConfigField "int", "default_theme", "2" } fdroid_full { applicationId "app.fedilab.tubelab" @@ -77,6 +83,9 @@ android { buildConfigField "boolean", "sepia_search", "true" buildConfigField "boolean", "instance_switcher", "true" buildConfigField "boolean", "allow_remote_connections", "true" + buildConfigField "boolean", "google_cast_lib", "false" + buildConfigField "int", "cast_enabled", "0" + buildConfigField "int", "default_theme", "2" } google_full { applicationId "app.fedilab.tubelab" @@ -89,6 +98,9 @@ android { buildConfigField "boolean", "sepia_search", "true" buildConfigField "boolean", "instance_switcher", "true" buildConfigField "boolean", "allow_remote_connections", "true" + buildConfigField "boolean", "google_cast_lib", "true" + buildConfigField "int", "cast_enabled", "1" + buildConfigField "int", "default_theme", "2" } queermotion { applicationId "org.queermotion.peertube" @@ -101,6 +113,9 @@ android { buildConfigField "boolean", "sepia_search", "false" buildConfigField "boolean", "instance_switcher", "false" buildConfigField "boolean", "allow_remote_connections", "false" + buildConfigField "boolean", "google_cast_lib", "false" + buildConfigField "int", "cast_enabled", "0" + buildConfigField "int", "default_theme", "2" } bittube { applicationId "app.fedilab.bittube" @@ -113,33 +128,38 @@ android { buildConfigField "boolean", "sepia_search", "false" buildConfigField "boolean", "instance_switcher", "true" buildConfigField "boolean", "allow_remote_connections", "false" + buildConfigField "boolean", "google_cast_lib", "true" + buildConfigField "int", "cast_enabled", "1" + buildConfigField "int", "default_theme", "1" } } sourceSets { fdroid_peertube_apps_educ { - res.srcDirs = ['src/main/res', 'src/acad/res'] - java.srcDirs = ['src/main/java', 'src/acad/java', 'src/no_google_donation/java'] + res.srcDirs = ['src/main/res', 'src/acad/res', 'src/no_google_cast_lib/res'] + java.srcDirs = ['src/main/java', 'src/acad/java', 'src/no_google_donation/java', 'src/no_google_cast_lib/java'] } google_peertube_apps_educ { - res.srcDirs = ['src/main/res', 'src/acad/res'] - java.srcDirs = ['src/main/java', 'src/acad/java', 'src/no_google_donation/java'] + res.srcDirs = ['src/main/res', 'src/acad/res', 'src/no_google_cast_lib/res'] + java.srcDirs = ['src/main/java', 'src/acad/java', 'src/no_google_donation/java', 'src/no_google_cast_lib/java'] } fdroid_full { - res.srcDirs = ['src/main/res', 'src/full/res'] - java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java'] + res.srcDirs = ['src/main/res', 'src/full/res', 'src/no_google_cast_lib/res'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java', 'src/no_google_cast_lib/java'] } google_full { - res.srcDirs = ['src/main/res', 'src/full/res', 'src/google_donation/res'] - java.srcDirs = ['src/main/java', 'src/full/java', 'src/google_donation/java'] + res.srcDirs = ['src/main/res', 'src/full/res', 'src/google_donation/res', 'src/google_cast_lib/res'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/google_donation/java', 'src/google_cast_lib/java'] + manifest.srcFile "src/google_cast_lib/AndroidManifest.xml" } queermotion { - res.srcDirs = ['src/main/res', 'src/queermotion/res'] - java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java'] + res.srcDirs = ['src/main/res', 'src/queermotion/res', 'src/no_google_cast_lib/res'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java', 'src/no_google_cast_lib/java'] } bittube { - res.srcDirs = ['src/main/res', 'src/bittube/res'] - java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java'] + res.srcDirs = ['src/main/res', 'src/bittube/res', 'src/google_cast_lib/res'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java', 'src/google_cast_lib/java'] + manifest.srcFile "src/google_cast_lib/AndroidManifest.xml" } } } @@ -179,6 +199,7 @@ dependencies { implementation "com.github.mabbas007:TagsEditText:1.0.5" implementation "com.github.bumptech.glide:glide:4.11.0" annotationProcessor "com.github.bumptech.glide:compiler:4.11.0" + implementation 'jp.wasabeef:glide-transformations:4.0.0' implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "net.gotev:uploadservice:4.5.1" implementation "net.gotev:uploadservice-okhttp:4.5.1" @@ -196,17 +217,34 @@ dependencies { implementation "androidx.work:work-runtime:2.4.0" implementation "androidx.work:work-runtime-ktx:2.4.0" - //custom cast feature - 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' - implementation 'org.slf4j:slf4j-simple:1.7.30' - + //************ DONATION GOOGLE ONLY **************// google_fullImplementation "com.android.billingclient:billing:3.0.2" + //************ MATOMO --> acad instances only **************// + fdroid_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2' google_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2' + //************ CAST **************/// + + //---> Google libs (google_full + bittube) + google_fullImplementation "androidx.mediarouter:mediarouter:1.2.1" + google_fullImplementation 'com.google.android.gms:play-services-cast-framework:19.0.0' + bittubeImplementation "androidx.mediarouter:mediarouter:1.2.1" + bittubeImplementation 'com.google.android.gms:play-services-cast-framework:19.0.0' + //----> Other flavors + fdroid_peertube_apps_educImplementation 'su.litvak.chromecast:api-v2:0.11.3' + fdroid_peertube_apps_educImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0' + fdroid_peertube_apps_educImplementation 'org.slf4j:slf4j-simple:1.7.30' + google_peertube_apps_educImplementation 'su.litvak.chromecast:api-v2:0.11.3' + google_peertube_apps_educImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0' + google_peertube_apps_educImplementation 'org.slf4j:slf4j-simple:1.7.30' + fdroid_fullImplementation 'su.litvak.chromecast:api-v2:0.11.3' + fdroid_fullImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0' + fdroid_fullImplementation 'org.slf4j:slf4j-simple:1.7.30' + queermotionImplementation 'su.litvak.chromecast:api-v2:0.11.3' + queermotionImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0' + queermotionImplementation 'org.slf4j:slf4j-simple:1.7.30' } \ No newline at end of file diff --git a/app/src/google_cast_lib/AndroidManifest.xml b/app/src/google_cast_lib/AndroidManifest.xml new file mode 100644 index 0000000..4e12e9a --- /dev/null +++ b/app/src/google_cast_lib/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/google_cast_lib/java/app/fedilab/fedilabtube/BaseMainActivity.java b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/BaseMainActivity.java new file mode 100644 index 0000000..1f5b39a --- /dev/null +++ b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/BaseMainActivity.java @@ -0,0 +1,41 @@ +package app.fedilab.fedilabtube; +/* 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 . */ + +import android.os.Bundle; +import android.view.View; + +import androidx.appcompat.app.AppCompatActivity; + +import app.fedilab.fedilabtube.databinding.ActivityMainBinding; + +public class BaseMainActivity extends AppCompatActivity { + + protected ActivityMainBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + binding = ActivityMainBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + } + + //Method for discovering cast devices + public void discoverCast() { + } + +} diff --git a/app/src/google_cast_lib/java/app/fedilab/fedilabtube/BasePeertubeActivity.java b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/BasePeertubeActivity.java new file mode 100644 index 0000000..4afe379 --- /dev/null +++ b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/BasePeertubeActivity.java @@ -0,0 +1,177 @@ +package app.fedilab.fedilabtube; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.View; + +import androidx.appcompat.app.AppCompatActivity; + +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.gms.cast.MediaInfo; +import com.google.android.gms.cast.MediaMetadata; +import com.google.android.gms.cast.framework.CastButtonFactory; +import com.google.android.gms.cast.framework.CastContext; +import com.google.android.gms.cast.framework.CastSession; +import com.google.android.gms.cast.framework.SessionManagerListener; +import com.google.android.gms.cast.framework.media.RemoteMediaClient; +import com.google.android.gms.common.images.WebImage; + +import app.fedilab.fedilabtube.client.data.VideoData; +import app.fedilab.fedilabtube.databinding.ActivityPeertubeBinding; +import app.fedilab.fedilabtube.helper.Helper; + +/* 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 . */ + +public class BasePeertubeActivity extends AppCompatActivity { + + protected ActivityPeertubeBinding binding; + protected VideoData.Video peertube; + protected SimpleExoPlayer player; + protected String videoURL; + protected String subtitlesStr; + + private CastContext mCastContext; + private CastSession mCastSession; + private SessionManagerListener mSessionManagerListener; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityPeertubeBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + + int search_cast = sharedpreferences.getInt(getString(R.string.set_cast_choice), BuildConfig.cast_enabled); + if (search_cast == 1) { + setupCastListener(); + mCastContext = CastContext.getSharedInstance(this); + mCastSession = mCastContext.getSessionManager().getCurrentCastSession(); + + } + + } + + + protected void loadCast() { + MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); + + movieMetadata.putString(MediaMetadata.KEY_TITLE, peertube.getTitle()); + if (subtitlesStr != null) { + movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, subtitlesStr); + } + movieMetadata.addImage(new WebImage(Uri.parse(peertube.getPreviewPath()))); + MediaInfo mediaInfo = new MediaInfo.Builder(videoURL) + .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) + .setMetadata(movieMetadata) + .setStreamDuration(peertube.getDuration() * 1000) + .build(); + if (mCastSession != null) { + RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); + remoteMediaClient.load(mediaInfo); + } + } + + + private void setupCastListener() { + mSessionManagerListener = new SessionManagerListener() { + @Override + public void onSessionStarting(CastSession castSession) { + } + + @Override + public void onSessionStarted(CastSession castSession, String s) { + onApplicationConnected(castSession); + } + + @Override + public void onSessionStartFailed(CastSession castSession, int i) { + onApplicationDisconnected(); + } + + @Override + public void onSessionEnding(CastSession castSession) { + onApplicationDisconnected(); + } + + @Override + public void onSessionEnded(CastSession castSession, int i) { + onApplicationDisconnected(); + } + + @Override + public void onSessionResuming(CastSession castSession, String s) { + } + + @Override + public void onSessionResumed(CastSession castSession, boolean b) { + onApplicationConnected(castSession); + } + + @Override + public void onSessionResumeFailed(CastSession castSession, int i) { + onApplicationDisconnected(); + } + + @Override + public void onSessionSuspended(CastSession castSession, int i) { + onApplicationDisconnected(); + } + + private void onApplicationConnected(CastSession castSession) { + mCastSession = castSession; + supportInvalidateOptionsMenu(); + player.setPlayWhenReady(false); + binding.doubleTapPlayerView.setVisibility(View.INVISIBLE); + loadCast(); + } + + private void onApplicationDisconnected() { + binding.doubleTapPlayerView.setVisibility(View.VISIBLE); + supportInvalidateOptionsMenu(); + } + }; + } + + @Override + protected void onResume() { + mCastContext.getSessionManager().addSessionManagerListener( + mSessionManagerListener, CastSession.class); + super.onResume(); + } + + @Override + protected void onPause() { + mCastContext.getSessionManager().removeSessionManagerListener( + mSessionManagerListener, CastSession.class); + super.onPause(); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.video_menu, menu); + CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), + menu, + R.id.media_route_button); + return true; + } +} diff --git a/app/src/google_cast_lib/java/app/fedilab/fedilabtube/expandedcontrols/ExpandedControlsActivity.java b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/expandedcontrols/ExpandedControlsActivity.java new file mode 100755 index 0000000..6195e6a --- /dev/null +++ b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/expandedcontrols/ExpandedControlsActivity.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Google LLC. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.fedilab.fedilabtube.expandedcontrols; + +import android.view.Menu; + +import com.google.android.gms.cast.framework.CastButtonFactory; +import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity; + +import app.fedilab.fedilabtube.R; + + +public class ExpandedControlsActivity extends ExpandedControllerActivity { + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.video_menu, menu); + CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_button); + return true; + } +} \ No newline at end of file diff --git a/app/src/google_cast_lib/java/app/fedilab/fedilabtube/provider/CastOptionsProvider.java b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/provider/CastOptionsProvider.java new file mode 100644 index 0000000..922aa3f --- /dev/null +++ b/app/src/google_cast_lib/java/app/fedilab/fedilabtube/provider/CastOptionsProvider.java @@ -0,0 +1,54 @@ +package app.fedilab.fedilabtube.provider; +/* 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 . */ + +import android.content.Context; + +import com.google.android.gms.cast.framework.CastOptions; +import com.google.android.gms.cast.framework.OptionsProvider; +import com.google.android.gms.cast.framework.SessionProvider; +import com.google.android.gms.cast.framework.media.CastMediaOptions; +import com.google.android.gms.cast.framework.media.NotificationOptions; + +import java.util.List; + +import app.fedilab.fedilabtube.BuildConfig; +import app.fedilab.fedilabtube.expandedcontrols.ExpandedControlsActivity; +import app.fedilab.fedilabtube.helper.Helper; + + +public class CastOptionsProvider implements OptionsProvider { + + + @Override + public CastOptions getCastOptions(Context context) { + NotificationOptions notificationOptions = new NotificationOptions.Builder() + .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) + .build(); + CastMediaOptions mediaOptions = new CastMediaOptions.Builder() + .setNotificationOptions(notificationOptions) + .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) + .build(); + return new CastOptions.Builder() + .setReceiverApplicationId(BuildConfig.FLAVOR.compareTo("bittube") == 0 ? Helper.CAST_ID_BITTUBE : Helper.CAST_ID) + .setCastMediaOptions(mediaOptions) + .build(); + } + + @Override + public List getAdditionalSessionProviders(Context context) { + return null; + } +} \ No newline at end of file diff --git a/app/src/google_cast_lib/res/menu/video_menu.xml b/app/src/google_cast_lib/res/menu/video_menu.xml new file mode 100644 index 0000000..1e36b9b --- /dev/null +++ b/app/src/google_cast_lib/res/menu/video_menu.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/java/app/fedilab/fedilabtube/BaseFedilabTube.java b/app/src/main/java/app/fedilab/fedilabtube/BaseFedilabTube.java index cd5bb4b..c075850 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/BaseFedilabTube.java +++ b/app/src/main/java/app/fedilab/fedilabtube/BaseFedilabTube.java @@ -65,7 +65,7 @@ public class BaseFedilabTube extends MultiDexApplication { MultiDex.install(BaseFedilabTube.this); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - int themePref = sharedpreferences.getInt(Helper.SET_THEME, Helper.DEFAULT_MODE); + int themePref = sharedpreferences.getInt(Helper.SET_THEME, BuildConfig.default_theme); ThemeHelper.switchTo(themePref); diff --git a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java index 25e494f..8f122a1 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java @@ -16,17 +16,14 @@ package app.fedilab.fedilabtube; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; 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; @@ -37,14 +34,12 @@ import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.TooltipCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; @@ -53,13 +48,7 @@ import com.kobakei.ratethisapp.RateThisApp; import org.jetbrains.annotations.NotNull; -import java.io.IOException; -import java.net.InetAddress; -import java.net.NetworkInterface; import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -73,7 +62,6 @@ import java.util.regex.Pattern; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.AccountData.Account; import app.fedilab.fedilabtube.client.data.InstanceData; -import app.fedilab.fedilabtube.client.data.VideoData; import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.PeertubeInformation; @@ -95,17 +83,13 @@ 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.ChromeCast; -import su.litvak.chromecast.api.v2.ChromeCasts; -import su.litvak.chromecast.api.v2.ChromeCastsListener; -import su.litvak.chromecast.api.v2.MediaStatus; import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.NORMAL; import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.SURFING; import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; -public class MainActivity extends AppCompatActivity implements ChromeCastsListener { +public class MainActivity extends BaseMainActivity { public static int PICK_INSTANCE = 5641; @@ -113,9 +97,8 @@ public class MainActivity extends AppCompatActivity implements ChromeCastsListen public static UserMe userMe; public static InstanceData.InstanceConfig instanceConfig; public static TypeOfConnection typeOfConnection; - public static List chromeCasts; - public static ChromeCast chromeCast; - public static boolean chromecastActivated = false; + + private DisplayVideosFragment recentFragment, locaFragment, trendingFragment, subscriptionFragment, mostLikedFragment; private DisplayOverviewFragment overviewFragment; private ActivityMainBinding binding; @@ -158,8 +141,7 @@ public class MainActivity extends AppCompatActivity implements ChromeCastsListen } return true; }; - private BroadcastReceiver manage_chromecast; - private VideoData.Video castedTube; + @SuppressLint("ApplySharedPref") public static void showRadioButtonDialogFullInstances(Activity activity, boolean storeInDb) { @@ -234,123 +216,20 @@ public class MainActivity extends AppCompatActivity implements ChromeCastsListen } } - @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(); binding = null; - ChromeCasts.unregisterListener(this); - if (manage_chromecast != null) { - LocalBroadcastManager.getInstance(MainActivity.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 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); - 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(); - } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityMainBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); - ChromeCastsListener chromeCastsListener = this; - ChromeCasts.registerListener(chromeCastsListener); + binding = super.binding; Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -462,92 +341,10 @@ public class MainActivity extends AppCompatActivity implements ChromeCastsListen } - 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(MainActivity.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(MainActivity.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(); - } - } - }; final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS)); - int search_cast = sharedpreferences.getInt(getString(R.string.set_cast_choice), 0); + + int search_cast = sharedpreferences.getInt(getString(R.string.set_cast_choice), BuildConfig.cast_enabled); if (search_cast == 1) { discoverCast(); } diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java index f105b1b..b1cfba5 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java @@ -44,7 +44,6 @@ 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; @@ -52,7 +51,6 @@ import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.view.inputmethod.InputMethodManager; -import android.webkit.MimeTypeMap; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; @@ -63,14 +61,12 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.PopupMenu; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.lifecycle.ViewModelProvider; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -111,8 +107,6 @@ import com.google.android.material.snackbar.Snackbar; 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; @@ -163,19 +157,12 @@ 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.ChromeCast; -import su.litvak.chromecast.api.v2.MediaStatus; -import su.litvak.chromecast.api.v2.Status; -import static app.fedilab.fedilabtube.MainActivity.chromeCast; -import static app.fedilab.fedilabtube.MainActivity.chromeCasts; -import static app.fedilab.fedilabtube.MainActivity.chromecastActivated; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.ADD_COMMENT; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.RATEVIDEO; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPLY; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_VIDEO; -import static app.fedilab.fedilabtube.helper.Helper.CAST_ID; import static app.fedilab.fedilabtube.helper.Helper.canMakeAction; import static app.fedilab.fedilabtube.helper.Helper.getAttColor; import static app.fedilab.fedilabtube.helper.Helper.isLoggedIn; @@ -184,15 +171,13 @@ 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 BasePeertubeActivity implements CommentListAdapter.AllCommentRemoved, Player.EventListener, VideoListener, TorrentListener, MenuAdapter.ItemClicked, MenuItemAdapter.ItemAction { public static String video_id; public static List playedVideos = new ArrayList<>(); private String peertubeInstance, videoUuid; private ImageView fullScreenIcon; - private SimpleExoPlayer player; private boolean fullScreenMode; - private VideoData.Video peertube; private int mode; private Map> playlists; private boolean playInMinimized, autoPlay, autoFullscreen; @@ -218,7 +203,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd private String currentCaption; private boolean isRemote; private boolean willPlayFromIntent; - private String chromeCastVideoURL; + private app.fedilab.fedilabtube.client.mastodon.Status status; Uri captionURI; String captionLang; @@ -307,10 +292,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - binding = ActivityPeertubeBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); + binding = super.binding; videoOrientationType = videoOrientation.LANDSCAPE; max_id = "0"; SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); @@ -512,34 +494,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } }); - binding.castPlay.setOnClickListener(v -> { - binding.castLoader.setVisibility(View.VISIBLE); - if (chromeCast != null) { - new Thread(() -> { - try { - 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) { - Handler mainHandler = new Handler(Looper.getMainLooper()); - int finalIcon = icon; - Runnable myRunnable = () -> binding.castPlay.setImageResource(finalIcon); - mainHandler.post(myRunnable); - } - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> binding.castLoader.setVisibility(View.GONE); - mainHandler.post(myRunnable); - } catch (IOException e) { - e.printStackTrace(); - } - }).start(); - } - }); + } @@ -798,20 +753,6 @@ 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) { @@ -821,101 +762,6 @@ 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++; - } - i = 0; - for (ChromeCast cc : chromeCasts) { - if (chromecastActivated && cc.isConnected()) { - break; - } - i++; - } - - alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> { - chromeCast = chromeCasts.get(position); - new Thread(() -> { - if (chromeCast != null) { - Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); - Bundle b = new Bundle(); - if (chromecastActivated) { - b.putInt("displayed", 0); - intentBC.putExtras(b); - LocalBroadcastManager.getInstance(PeertubeActivity.this).sendBroadcast(intentBC); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - binding.doubleTapPlayerView.setVisibility(View.VISIBLE); - binding.castController.setVisibility(View.GONE); - }; - mainHandler.post(myRunnable); - - } else { - b.putInt("displayed", 1); - b.putParcelable("castedTube", peertube); - intentBC.putExtras(b); - LocalBroadcastManager.getInstance(PeertubeActivity.this).sendBroadcast(intentBC); - try { - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - invalidateOptionsMenu(); - binding.castLoader.setVisibility(View.VISIBLE); - player.setPlayWhenReady(false); - binding.doubleTapPlayerView.setVisibility(View.GONE); - binding.castController.setVisibility(View.VISIBLE); - dialog.dismiss(); - if (chromeCastVideoURL != null) { - if (player != null && player.getCurrentPosition() > 0) { - chromeCastVideoURL += "?start=" + (player.getCurrentPosition() / 1000); - } - } - }; - mainHandler.post(myRunnable); - if (!chromeCast.isConnected()) { - chromeCast.connect(); - } - myRunnable = this::invalidateOptionsMenu; - mainHandler.post(myRunnable); - Status status = chromeCast.getStatus(); - if (chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) { - chromeCast.launchApp(CAST_ID); - } - - if (chromeCastVideoURL != null) { - String mime = MimeTypeMap.getFileExtensionFromUrl(chromeCastVideoURL); - chromeCast.setRequestTimeout(60000); - chromeCast.load(peertube.getTitle(), null, chromeCastVideoURL, mime); - chromeCast.play(); - binding.castPlay.setImageResource(R.drawable.ic_baseline_pause_32); - } - myRunnable = () -> binding.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); } @@ -1417,6 +1263,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd }); } + /** * Manage video to play with different factors * @@ -1429,7 +1276,10 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd * @param lang String ("en","fr", etc.) */ private void stream(VideoData.Video video, String localTorrentUrl, String resolution, boolean autoPlay, long position, Uri subtitles, String lang) { - String videoURL = localTorrentUrl == null ? video.getFileUrl(resolution, PeertubeActivity.this) : localTorrentUrl; + videoURL = localTorrentUrl == null ? video.getFileUrl(resolution, PeertubeActivity.this) : localTorrentUrl; + if (subtitles != null) { + subtitlesStr = subtitles.toString(); + } SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); if (videoURL != null && (videoURL.endsWith(".torrent") || videoURL.startsWith("magnet"))) { @@ -1503,6 +1353,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd if (autoPlay) { binding.doubleTapPlayerView.hideController(); } + // loadCast(video, videoURL, subtitles!=null?subtitles.toString():null); } private void fetchComments() { @@ -1544,7 +1395,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd snackbar.show(); return; } - 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)) { diff --git a/app/src/main/java/app/fedilab/fedilabtube/fragment/SettingsFragment.java b/app/src/main/java/app/fedilab/fedilabtube/fragment/SettingsFragment.java index b61a599..5f4bd17 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/fragment/SettingsFragment.java +++ b/app/src/main/java/app/fedilab/fedilabtube/fragment/SettingsFragment.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import app.fedilab.fedilabtube.BuildConfig; import app.fedilab.fedilabtube.MainActivity; import app.fedilab.fedilabtube.R; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; @@ -315,7 +316,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared CharSequence[] entriesTheme = arrayTheme.toArray(new CharSequence[0]); CharSequence[] entryValuesTheme = new CharSequence[3]; final SharedPreferences sharedpref = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - int currentTheme = sharedpref.getInt(Helper.SET_THEME, Helper.DEFAULT_MODE); + int currentTheme = sharedpref.getInt(Helper.SET_THEME, BuildConfig.default_theme); entryValuesTheme[0] = String.valueOf(Helper.LIGHT_MODE); entryValuesTheme[1] = String.valueOf(Helper.DARK_MODE); entryValuesTheme[2] = String.valueOf(Helper.DEFAULT_MODE); @@ -407,7 +408,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared set_video_in_list_choice.setChecked(videosInList); //****** Allow Chromecast ******* - int cast = sharedpref.getInt(getString(R.string.set_cast_choice), 0); + int cast = sharedpref.getInt(getString(R.string.set_cast_choice), BuildConfig.cast_enabled); SwitchPreference set_cast_choice = findPreference(getString(R.string.set_cast_choice)); assert set_cast_choice != null; set_cast_choice.setChecked(cast == 1); diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java index 5f0846d..309be0a 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java @@ -135,6 +135,7 @@ public class Helper { public static final String VIDEO_ID = "video_id_update"; public static final String APP_PREFS = "app_prefs"; public static final String CAST_ID = "D402501A"; + public static final String CAST_ID_BITTUBE = "CBA4A31D"; public static final int VIDEOS_PER_PAGE = 10; public static final String RECEIVE_ACTION = "receive_action"; public static final String SET_UNFOLLOW_VALIDATION = "set_unfollow_validation"; diff --git a/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java b/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java index 38d498d..4cacb52 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java +++ b/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java @@ -244,7 +244,7 @@ public class AccountDAO { public List getAllPeertubeAccount() { try { - Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_SOFTWARE + "=? OR " + Sqlite.COL_SOFTWARE + "is null", new String[]{"PEERTUBE"}, null, null, Sqlite.COL_INSTANCE + " ASC", null); + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_SOFTWARE + "=? OR " + Sqlite.COL_SOFTWARE + " is null", new String[]{"PEERTUBE"}, null, null, Sqlite.COL_INSTANCE + " ASC", null); return cursorToListUser(c); } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/res/layout/activity_peertube.xml b/app/src/main/res/layout/activity_peertube.xml index e79789d..287458a 100644 --- a/app/src/main/res/layout/activity_peertube.xml +++ b/app/src/main/res/layout/activity_peertube.xml @@ -689,6 +689,5 @@ android:layout_gravity="center" android:layout_margin="30dp" /> - diff --git a/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/BaseMainActivity.java b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/BaseMainActivity.java new file mode 100644 index 0000000..46c3bb8 --- /dev/null +++ b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/BaseMainActivity.java @@ -0,0 +1,261 @@ +package app.fedilab.fedilabtube; +/* 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 . */ + +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.fedilabtube.client.data.VideoData; +import app.fedilab.fedilabtube.databinding.ActivityMainBinding; +import app.fedilab.fedilabtube.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 BaseMainActivity extends AppCompatActivity implements ChromeCastsListener { + + public static List chromeCasts; + public static ChromeCast chromeCast; + public static boolean chromecastActivated = false; + protected ActivityMainBinding binding; + private BroadcastReceiver manage_chromecast; + private VideoData.Video castedTube; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityMainBinding.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(BaseMainActivity.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(BaseMainActivity.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(BaseMainActivity.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(BaseMainActivity.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 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); + 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(); + } + + +} diff --git a/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/BasePeertubeActivity.java b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/BasePeertubeActivity.java new file mode 100644 index 0000000..dc695b7 --- /dev/null +++ b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/BasePeertubeActivity.java @@ -0,0 +1,206 @@ +package app.fedilab.fedilabtube; + +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.appcompat.app.AppCompatActivity; +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.fedilabtube.client.data.VideoData; +import app.fedilab.fedilabtube.databinding.ActivityPeertubeBinding; +import app.fedilab.fedilabtube.helper.Helper; +import su.litvak.chromecast.api.v2.ChromeCast; +import su.litvak.chromecast.api.v2.MediaStatus; +import su.litvak.chromecast.api.v2.Status; + +import static app.fedilab.fedilabtube.helper.Helper.CAST_ID; + +/* 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 . */ + +public class BasePeertubeActivity extends AppCompatActivity { + + protected ActivityPeertubeBinding binding; + private String chromeCastVideoURL; + 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.castPlay.setOnClickListener(v -> { + binding.castLoader.setVisibility(View.VISIBLE); + if (BaseMainActivity.chromeCast != null) { + new Thread(() -> { + try { + int icon = -1; + if (BaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { + BaseMainActivity.chromeCast.pause(); + icon = R.drawable.ic_baseline_play_arrow_32; + } else if (BaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { + BaseMainActivity.chromeCast.play(); + icon = R.drawable.ic_baseline_pause_32; + } + if (icon != -1) { + Handler mainHandler = new Handler(Looper.getMainLooper()); + int finalIcon = icon; + Runnable myRunnable = () -> binding.castPlay.setImageResource(finalIcon); + mainHandler.post(myRunnable); + } + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> binding.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 (BaseMainActivity.chromeCasts != null && BaseMainActivity.chromeCasts.size() > 0) { + String[] chromecast_choice = new String[BaseMainActivity.chromeCasts.size()]; + AlertDialog.Builder alt_bld = new AlertDialog.Builder(this); + alt_bld.setTitle(R.string.chromecast_choice); + int i = 0; + for (ChromeCast cc : BaseMainActivity.chromeCasts) { + chromecast_choice[i] = cc.getTitle(); + i++; + } + i = 0; + for (ChromeCast cc : BaseMainActivity.chromeCasts) { + if (BaseMainActivity.chromecastActivated && cc.isConnected()) { + break; + } + i++; + } + + alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> { + BaseMainActivity.chromeCast = BaseMainActivity.chromeCasts.get(position); + new Thread(() -> { + if (BaseMainActivity.chromeCast != null) { + Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); + Bundle b = new Bundle(); + if (BaseMainActivity.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.castController.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.castLoader.setVisibility(View.VISIBLE); + player.setPlayWhenReady(false); + binding.doubleTapPlayerView.setVisibility(View.GONE); + binding.castController.setVisibility(View.VISIBLE); + dialog.dismiss(); + if (chromeCastVideoURL != null) { + if (player != null && player.getCurrentPosition() > 0) { + chromeCastVideoURL += "?start=" + (player.getCurrentPosition() / 1000); + } + } + }; + mainHandler.post(myRunnable); + if (!BaseMainActivity.chromeCast.isConnected()) { + BaseMainActivity.chromeCast.connect(); + } + myRunnable = this::invalidateOptionsMenu; + mainHandler.post(myRunnable); + Status status = BaseMainActivity.chromeCast.getStatus(); + if (BaseMainActivity.chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) { + BaseMainActivity.chromeCast.launchApp(CAST_ID); + } + if (chromeCastVideoURL != null) { + String mime = MimeTypeMap.getFileExtensionFromUrl(chromeCastVideoURL); + BaseMainActivity.chromeCast.setRequestTimeout(60000); + BaseMainActivity.chromeCast.load(peertube.getTitle(), null, chromeCastVideoURL, mime); + BaseMainActivity.chromeCast.play(); + binding.castPlay.setImageResource(R.drawable.ic_baseline_pause_32); + } + myRunnable = () -> binding.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 (BaseMainActivity.chromeCasts != null && BaseMainActivity.chromeCasts.size() > 0) { + castItem.setVisible(true); + if (BaseMainActivity.chromeCast != null && BaseMainActivity.chromeCast.isConnected()) { + castItem.setIcon(R.drawable.ic_baseline_cast_connected_24); + } else { + castItem.setIcon(R.drawable.ic_baseline_cast_24); + } + } + return true; + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/DashCastRequest.java b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/DashCastRequest.java similarity index 100% rename from app/src/main/java/app/fedilab/fedilabtube/helper/DashCastRequest.java rename to app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/DashCastRequest.java diff --git a/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/provider/CastOptionsProvider.java b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/provider/CastOptionsProvider.java new file mode 100644 index 0000000..70d5277 --- /dev/null +++ b/app/src/no_google_cast_lib/java/app/fedilab/fedilabtube/provider/CastOptionsProvider.java @@ -0,0 +1,18 @@ +package app.fedilab.fedilabtube.provider; +/* 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 . */ + +public class CastOptionsProvider { +} \ No newline at end of file diff --git a/app/src/main/res/menu/video_menu.xml b/app/src/no_google_cast_lib/res/menu/video_menu.xml similarity index 100% rename from app/src/main/res/menu/video_menu.xml rename to app/src/no_google_cast_lib/res/menu/video_menu.xml