diff --git a/CHANGELOG.md b/CHANGELOG.md index f6e2563..d2fe13a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### Version 1.0.44 Tag: v1.0.44 (2020-07-05) + * Completed implementation of Likes & Dislikes (@Poslovitch) + * Added preview of the current playback speed and video quality in the VideoOptionsFragment (@Poslovitch) + * Lots of code cleanup + * Various translations + ### Version 1.0.43 Tag: v1.0.43 (2020-07-04) * Fix back button issue diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..04ef9f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM gradle:5.6.4-jdk8 + +ENV ANDROID_SDK_URL https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip +ENV ANDROID_BUILD_TOOLS_VERSION 29.0.3 +ENV ANDROID_HOME /usr/local/android-sdk-linux +ENV ANDROID_VERSION 29 +ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools + +RUN mkdir "$ANDROID_HOME" .android && \ + cd "$ANDROID_HOME" && \ + curl -o sdk.zip $ANDROID_SDK_URL && \ + unzip sdk.zip && \ + rm sdk.zip + +RUN yes | ${ANDROID_HOME}/tools/bin/sdkmanager --licenses +RUN $ANDROID_HOME/tools/bin/sdkmanager --update +RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \ + "platforms;android-${ANDROID_VERSION}" \ + "platform-tools" \ No newline at end of file diff --git a/REPRODUCIBLE_BUILDS.md b/REPRODUCIBLE_BUILDS.md new file mode 100644 index 0000000..00bf5ef --- /dev/null +++ b/REPRODUCIBLE_BUILDS.md @@ -0,0 +1,61 @@ +# Reproducible Builds + +Note: reproducible builds work starting version 1.0.44 + +## Install Docker + +Download and install [Docker](https://www.docker.com/). + +## Check your Thorium app version and build timestamp + +1. Open the Thorium app +2. Go to Settings +3. Check the app version listed under About 'Version' (e.g., 1.0.44), and record its value to be used later +4. Check the build timestamp under About 'Build Time' (e.g., 1593942384524), and record its value to be used later + +## Download the App open-source code + +1. Make sure you have `git` installed +2. Clone the Github repository +3. Checkout the Tag that corresponds to the version of your Thorium app (e.g., 1.0.44) + +```shell +git clone https://github.com/sschueller/peertube-android ~/peertube-android +cd ~/peertube-android +git checkout v1.0.44 +``` + +## Build the project using Docker + +1. Build a Docker Image with the required Android Tools +2. Build the App in the Docker Container while specifying the build timestamp that was recorded earlier (e.g., 1593942384524) +3. Copy the freshly-built APK + +```shell +cd ~/peertube-android +docker build -t thorium-builder . +docker run --rm -v ~/peertube-android:/home/peertube-android -w /home/peertube-android thorium-builder gradle assembleProdRelease -PkeystorePassword=securePassword -PkeyAliasPassword=securePassword -PkeystoreFile=build.keystore -PbuildTimestamp=1593942384524 +cp app/build/outputs/apk/prod/release/app-prod-release.apk thorium-built.apk +``` + +## Extract the Play Store APK from your phone + +1. Make sure you have `adb` installed +2. Connect your phone to your computer +3. Extract the APK from the phone + +```shell +cd ~/peertube-android +adb pull `adb shell pm path net.schueller.peertube | cut -d':' -f2` thorium-store.apk +``` + +## Compare the two files + +1. Make sure you have `python` installed +2. Use the `apkdiff` script to compare the APKs + +```shell +cd ~/peertube-android +python apkdiff.py thorium-built.apk thorium-store.apk +``` + diff --git a/app/build.gradle b/app/build.gradle index 0751f3e..4f789c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,33 @@ apply plugin: 'com.android.application' +ext.readProperty = { paramName -> readPropertyWithDefault(paramName, null) } +ext.readPropertyWithDefault = { paramName, defaultValue -> + if (project.hasProperty(paramName)) { + return project.getProperties().get(paramName) + } else { + Properties properties = new Properties() + if (project.rootProject.file('local.properties').exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } + if (properties.getProperty(paramName) != null) { + return properties.getProperty(paramName) + } else { + return defaultValue + } + } +} + android { compileSdkVersion 29 defaultConfig { applicationId "net.schueller.peertube" minSdkVersion 21 targetSdkVersion 29 - versionCode 1043 - versionName "1.0.43" + versionCode 1044 + versionName "1.0.44" + //buildTime readPropertyWithDefault('buildTimestamp', System.currentTimeMillis()) + 'L' + //buildConfigField "long", "BUILD_TIME", readPropertyWithDefault('buildTimestamp', System.currentTimeMillis()) + 'L' + //resValue "string", "BUILD_TIME", readPropertyWithDefault('buildTimestamp', System.currentTimeMillis()) + 'L' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ext { libVersions = [ @@ -87,6 +107,7 @@ android { applicationVariants.all { variant -> variant.resValue "string", "versionName", variant.versionName + variant.resValue "string", "buildTime", readPropertyWithDefault('buildTimestamp', System.currentTimeMillis()) + '' } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee09a9b..3091705 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,7 +49,7 @@ android:label="@string/title_activity_settings" android:theme="@style/AppTheme.NoActionBar" /> - * - * License: GPL-3.0+ - * 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. - * - * This program 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 this program. If not, see . - */ - -package net.schueller.peertube.activity; - -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.os.Bundle; -import android.preference.PreferenceActivity; -import androidx.annotation.LayoutRes; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatDelegate; - -import android.preference.PreferenceManager; -import android.view.MenuInflater; -import android.view.View; -import android.view.ViewGroup; - -import static net.schueller.peertube.helper.Constants.DEFAULT_THEME; -import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY; - -/** - * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls - * to be used with AppCompat. - */ -public abstract class AppCompatPreferenceActivity extends PreferenceActivity { - - private AppCompatDelegate mDelegate; - - @Override - protected void onCreate(Bundle savedInstanceState) { - getDelegate().installViewFactory(); - getDelegate().onCreate(savedInstanceState); - super.onCreate(savedInstanceState); - - // TODO: cleanup this duplication - - // Set Night Mode - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); - AppCompatDelegate.setDefaultNightMode(sharedPref.getBoolean("pref_dark_mode", false) ? - AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO); - - // Set theme - setTheme(getResources().getIdentifier( - sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME), - "style", - getPackageName()) - ); - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - getDelegate().onPostCreate(savedInstanceState); - } - - public ActionBar getSupportActionBar() { - return getDelegate().getSupportActionBar(); - } - -// public void setSupportActionBar(@Nullable Toolbar toolbar) { -// getDelegate().setSupportActionBar(toolbar); -// } - - @Override - public MenuInflater getMenuInflater() { - return getDelegate().getMenuInflater(); - } - - @Override - public void setContentView(@LayoutRes int layoutResID) { - getDelegate().setContentView(layoutResID); - } - - @Override - public void setContentView(View view) { - getDelegate().setContentView(view); - } - - @Override - public void setContentView(View view, ViewGroup.LayoutParams params) { - getDelegate().setContentView(view, params); - } - - @Override - public void addContentView(View view, ViewGroup.LayoutParams params) { - getDelegate().addContentView(view, params); - } - - @Override - protected void onPostResume() { - super.onPostResume(); - getDelegate().onPostResume(); - } - - @Override - protected void onTitleChanged(CharSequence title, int color) { - super.onTitleChanged(title, color); - getDelegate().setTitle(title); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - getDelegate().onConfigurationChanged(newConfig); - } - - @Override - protected void onStop() { - super.onStop(); - getDelegate().onStop(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - getDelegate().onDestroy(); - } - - public void invalidateOptionsMenu() { - getDelegate().invalidateOptionsMenu(); - } - - private AppCompatDelegate getDelegate() { - if (mDelegate == null) { - mDelegate = AppCompatDelegate.create(this, null); - } - return mDelegate; - } -} diff --git a/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java b/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java index a733fca..c0fdaff 100644 --- a/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java @@ -26,10 +26,9 @@ import android.preference.PreferenceManager; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; -import java.util.Locale; +import net.schueller.peertube.R; -import static net.schueller.peertube.helper.Constants.DEFAULT_THEME; -import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY; +import java.util.Locale; public class CommonActivity extends AppCompatActivity { @@ -39,23 +38,28 @@ public class CommonActivity extends AppCompatActivity { // Set Night Mode SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); - AppCompatDelegate.setDefaultNightMode(sharedPref.getBoolean("pref_dark_mode", false) ? + AppCompatDelegate.setDefaultNightMode(sharedPref.getBoolean(getString(R.string.pref_dark_mode_key), false) ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO); // Set theme setTheme(getResources().getIdentifier( - sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME), + sharedPref.getString( + getString(R.string.pref_theme_key), + getString(R.string.app_default_theme) + ), "style", getPackageName()) ); // Set language - String countryCode=sharedPref.getString("pref_language_app","en"); - Locale locale=new Locale(countryCode);; + String countryCode = sharedPref.getString(getString(R.string.pref_language_app_key), "en"); + assert countryCode != null; + Locale locale = new Locale(countryCode); + //Neither Chinese language choice was working, found this fix on stack overflow - if(countryCode.equals("zh-rCN")) + if (countryCode.equals("zh-rCN")) locale = Locale.SIMPLIFIED_CHINESE; - if(countryCode.equals("zh-rTW")) + if (countryCode.equals("zh-rTW")) locale = Locale.TRADITIONAL_CHINESE; Locale.setDefault(locale); diff --git a/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java b/app/src/main/java/net/schueller/peertube/activity/SearchServerActivity.java similarity index 85% rename from app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java rename to app/src/main/java/net/schueller/peertube/activity/SearchServerActivity.java index a4c6186..6c57cf7 100644 --- a/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/SearchServerActivity.java @@ -18,7 +18,6 @@ package net.schueller.peertube.activity; import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -27,36 +26,25 @@ import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; -import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.util.Log; -import android.util.Patterns; import android.view.View; -import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import net.schueller.peertube.R; -import net.schueller.peertube.adapter.ServerAdapter; -import net.schueller.peertube.adapter.VideoAdapter; +import net.schueller.peertube.adapter.ServerSearchAdapter; import net.schueller.peertube.helper.APIUrlHelper; import net.schueller.peertube.model.ServerList; -import net.schueller.peertube.model.VideoList; import net.schueller.peertube.network.GetServerListDataService; -import net.schueller.peertube.network.GetVideoDataService; import net.schueller.peertube.network.RetrofitInstance; import java.util.ArrayList; import java.util.Objects; -import static net.schueller.peertube.helper.Constants.DEFAULT_THEME; -import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY; +public class SearchServerActivity extends CommonActivity { -public class SelectServerActivity extends CommonActivity { - - private ServerAdapter serverAdapter; + private ServerSearchAdapter serverAdapter; private SwipeRefreshLayout swipeRefreshLayout; private int currentStart = 0; @@ -76,7 +64,7 @@ public class SelectServerActivity extends CommonActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_server_selection); + setContentView(R.layout.activity_search_server); // Attaching the layout to the toolbar object Toolbar toolbar = findViewById(R.id.tool_bar_server_selection); @@ -97,10 +85,10 @@ public class SelectServerActivity extends CommonActivity { emptyView = findViewById(R.id.empty_server_selection_view); - RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(SelectServerActivity.this); + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(SearchServerActivity.this); recyclerView.setLayoutManager(layoutManager); - serverAdapter = new ServerAdapter(new ArrayList<>(), this); + serverAdapter = new ServerSearchAdapter(new ArrayList<>(), this); recyclerView.setAdapter(serverAdapter); loadServers(currentStart, count); @@ -144,7 +132,7 @@ public class SelectServerActivity extends CommonActivity { isLoading = true; GetServerListDataService service = RetrofitInstance.getRetrofitInstance( - APIUrlHelper.getServerIndexUrl(SelectServerActivity.this) + APIUrlHelper.getServerIndexUrl(SearchServerActivity.this) ).create(GetServerListDataService.class); @@ -183,7 +171,7 @@ public class SelectServerActivity extends CommonActivity { @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { Log.wtf("err", t.fillInStackTrace()); - Toast.makeText(SelectServerActivity.this, getString(R.string.api_error), Toast.LENGTH_SHORT).show(); + Toast.makeText(SearchServerActivity.this, getString(R.string.api_error), Toast.LENGTH_SHORT).show(); isLoading = false; swipeRefreshLayout.setRefreshing(false); } diff --git a/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java index 38126d2..6f559b2 100644 --- a/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java @@ -25,8 +25,6 @@ import androidx.preference.PreferenceFragmentCompat; import net.schueller.peertube.R; -import java.util.Objects; - public class SettingsActivity extends CommonActivity { @Override diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java index c4ca315..b00bfa5 100644 --- a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java @@ -50,7 +50,6 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -320,8 +319,8 @@ public class VideoListActivity extends CommonActivity { isLoading = true; SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); - String nsfw = sharedPref.getBoolean("pref_show_nsfw", false) ? "both" : "false"; - Set languages = sharedPref.getStringSet("pref_language", null); + String nsfw = sharedPref.getBoolean(getString(R.string.pref_show_nsfw_key), false) ? "both" : "false"; + Set languages = sharedPref.getStringSet(getString(R.string.pref_video_language_key), null); String apiBaseURL = APIUrlHelper.getUrlWithVersion(this); GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); @@ -383,7 +382,7 @@ public class VideoListActivity extends CommonActivity { // only check when we actually need the permission SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && - sharedPref.getBoolean("pref_torrent_player", false)) { + sharedPref.getBoolean(getString(R.string.pref_torrent_player_key), false)) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } } diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java b/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java index f241d8b..398312f 100644 --- a/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java @@ -36,6 +36,7 @@ import android.os.Bundle; import android.preference.PreferenceManager; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; @@ -56,26 +57,25 @@ import net.schueller.peertube.service.VideoPlayerService; import java.util.ArrayList; -import java.util.Objects; import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; -//import static net.schueller.peertube.helper.Constants.BACKGROUND_PLAY_PREF_KEY; import static com.google.android.exoplayer2.ui.PlayerNotificationManager.ACTION_PAUSE; import static com.google.android.exoplayer2.ui.PlayerNotificationManager.ACTION_PLAY; import static com.google.android.exoplayer2.ui.PlayerNotificationManager.ACTION_STOP; -import static net.schueller.peertube.helper.Constants.BACKGROUND_AUDIO; -import static net.schueller.peertube.helper.Constants.DEFAULT_THEME; -import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY; +import static net.schueller.peertube.helper.VideoHelper.canEnterPipMode; public class VideoPlayActivity extends AppCompatActivity { private static final String TAG = "VideoPlayActivity"; - private static boolean floatMode = false; + static boolean floatMode = false; + private static final int REQUEST_CODE = 101; private BroadcastReceiver receiver; + //This can only be called when in entering pip mode which can't happen if the device doesn't support pip mode. @SuppressLint("NewApi") public void makePipControls() { @@ -84,7 +84,7 @@ public class VideoPlayActivity extends AppCompatActivity { ArrayList actions = new ArrayList<>(); - Intent actionIntent = new Intent(BACKGROUND_AUDIO); + Intent actionIntent = new Intent(getString(R.string.app_background_audio)); PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), REQUEST_CODE, actionIntent, 0); @SuppressLint({"NewApi", "LocalSuppress"}) Icon icon = Icon.createWithResource(getApplicationContext(), android.R.drawable.stat_sys_speakerphone); @SuppressLint({"NewApi", "LocalSuppress"}) RemoteAction remoteAction = new RemoteAction(icon, "close pip", "from pip window custom command", pendingIntent); @@ -96,21 +96,21 @@ public class VideoPlayActivity extends AppCompatActivity { remoteAction = new RemoteAction(icon, "play", "stop the media", pendingIntent); actions.add(remoteAction); - if (videoPlayerFragment.isPaused()){ - Log.e(TAG,"setting actions with play button"); + assert videoPlayerFragment != null; + if (videoPlayerFragment.isPaused()) { + Log.e(TAG, "setting actions with play button"); actionIntent = new Intent(ACTION_PLAY); pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), REQUEST_CODE, actionIntent, 0); icon = Icon.createWithResource(getApplicationContext(), com.google.android.exoplayer2.ui.R.drawable.exo_notification_play); remoteAction = new RemoteAction(icon, "play", "play the media", pendingIntent); - actions.add(remoteAction); } else { - Log.e(TAG,"setting actions with pause button"); + Log.e(TAG, "setting actions with pause button"); actionIntent = new Intent(ACTION_PAUSE); pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), REQUEST_CODE, actionIntent, 0); icon = Icon.createWithResource(getApplicationContext(), com.google.android.exoplayer2.ui.R.drawable.exo_notification_pause); remoteAction = new RemoteAction(icon, "pause", "pause the media", pendingIntent); - actions.add(remoteAction); } + actions.add(remoteAction); //add custom actions to pip window @@ -119,12 +119,13 @@ public class VideoPlayActivity extends AppCompatActivity { .setActions(actions) .build(); setPictureInPictureParams(params); - } + public void changedToPipMode() { FragmentManager fragmentManager = getSupportFragmentManager(); VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment); + assert videoPlayerFragment != null; videoPlayerFragment.showControls(false); //create custom actions makePipControls(); @@ -134,11 +135,12 @@ public class VideoPlayActivity extends AppCompatActivity { filter.addAction(ACTION_STOP); filter.addAction(ACTION_PAUSE); filter.addAction(ACTION_PLAY); - filter.addAction((BACKGROUND_AUDIO)); + filter.addAction((getString(R.string.app_background_audio))); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + assert action != null; if (action.equals(ACTION_PAUSE)) { videoPlayerFragment.pauseVideo(); makePipControls(); @@ -148,7 +150,7 @@ public class VideoPlayActivity extends AppCompatActivity { makePipControls(); } - if (action.equals(BACKGROUND_AUDIO)) { + if (action.equals(getString(R.string.app_background_audio))) { unregisterReceiver(receiver); finish(); } @@ -161,20 +163,23 @@ public class VideoPlayActivity extends AppCompatActivity { registerReceiver(receiver, filter); Log.v(TAG, "switched to pip "); - floatMode=true; + floatMode = true; videoPlayerFragment.showControls(false); } - public void changedToNormalMode(){ + + public void changedToNormalMode() { FragmentManager fragmentManager = getSupportFragmentManager(); VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment); + assert videoPlayerFragment != null; videoPlayerFragment.showControls(true); if (receiver != null) { unregisterReceiver(receiver); } - Log.v(TAG,"switched to normal"); - floatMode=false; + Log.v(TAG, "switched to normal"); + floatMode = false; } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -182,7 +187,10 @@ public class VideoPlayActivity extends AppCompatActivity { // Set theme SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); setTheme(getResources().getIdentifier( - sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME), + sharedPref.getString( + getString(R.string.pref_theme_key), + getString(R.string.app_default_theme) + ), "style", getPackageName()) ); @@ -197,17 +205,17 @@ public class VideoPlayActivity extends AppCompatActivity { assert videoPlayerFragment != null; String playingVideo = videoPlayerFragment.getVideoUuid(); - Log.v(TAG, "oncreate click: " + videoUuid +" is trying to replace: "+playingVideo); + Log.v(TAG, "oncreate click: " + videoUuid + " is trying to replace: " + playingVideo); - if (TextUtils.isEmpty(playingVideo)){ - Log.v(TAG,"oncreate no video currently playing"); + if (TextUtils.isEmpty(playingVideo)) { + Log.v(TAG, "oncreate no video currently playing"); videoPlayerFragment.start(videoUuid); - } else if(!playingVideo.equals(videoUuid)){ - Log.v(TAG,"oncreate different video playing currently"); + } else if (!playingVideo.equals(videoUuid)) { + Log.v(TAG, "oncreate different video playing currently"); videoPlayerFragment.stopVideo(); videoPlayerFragment.start(videoUuid); } else { - Log.v(TAG,"oncreate same video playing currently"); + Log.v(TAG, "oncreate same video playing currently"); } // if we are in landscape set the video to fullscreen @@ -225,19 +233,18 @@ public class VideoPlayActivity extends AppCompatActivity { getSupportFragmentManager().findFragmentById(R.id.video_player_fragment); assert videoPlayerFragment != null; String videoUuid = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID); - Log.v(TAG, "new intent click: " + videoUuid +" is trying to replace: "+videoPlayerFragment.getVideoUuid()); - assert videoPlayerFragment != null; + Log.v(TAG, "new intent click: " + videoUuid + " is trying to replace: " + videoPlayerFragment.getVideoUuid()); String playingVideo = videoPlayerFragment.getVideoUuid(); - if (TextUtils.isEmpty(playingVideo)){ - Log.v(TAG,"new intent no video currently playing"); + if (TextUtils.isEmpty(playingVideo)) { + Log.v(TAG, "new intent no video currently playing"); videoPlayerFragment.start(videoUuid); - } else if(!playingVideo.equals(videoUuid)){ - Log.v(TAG,"new intent different video playing currently"); + } else if (!playingVideo.equals(videoUuid)) { + Log.v(TAG, "new intent different video playing currently"); videoPlayerFragment.stopVideo(); videoPlayerFragment.start(videoUuid); } else { - Log.v(TAG,"new intent same video playing currently"); + Log.v(TAG, "new intent same video playing currently"); } // if we are in landscape set the video to fullscreen @@ -245,12 +252,10 @@ public class VideoPlayActivity extends AppCompatActivity { if (orientation == Configuration.ORIENTATION_LANDSCAPE) { setOrientation(true); } - } @Override - public void onConfigurationChanged(Configuration newConfig) { - + public void onConfigurationChanged(@NonNull Configuration newConfig) { Log.v(TAG, "onConfigurationChanged()..."); super.onConfigurationChanged(newConfig); @@ -263,58 +268,44 @@ public class VideoPlayActivity extends AppCompatActivity { } } - - private void setOrientation(Boolean isLandscape) { - FragmentManager fragmentManager = getSupportFragmentManager(); VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment); VideoMetaDataFragment videoMetaFragment = (VideoMetaDataFragment) fragmentManager.findFragmentById(R.id.video_meta_data_fragment); - if (isLandscape) { - assert videoPlayerFragment != null; - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) Objects.requireNonNull(videoPlayerFragment.getView()).getLayoutParams(); - params.width = FrameLayout.LayoutParams.MATCH_PARENT; - params.height = FrameLayout.LayoutParams.MATCH_PARENT; - videoPlayerFragment.getView().setLayoutParams(params); + assert videoPlayerFragment != null; + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) videoPlayerFragment.requireView().getLayoutParams(); + params.width = FrameLayout.LayoutParams.MATCH_PARENT; + params.height = isLandscape ? FrameLayout.LayoutParams.MATCH_PARENT : (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 250, getResources().getDisplayMetrics()); - if (videoMetaFragment != null) { - fragmentManager.beginTransaction() - .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out) - .hide(videoMetaFragment) - .commit(); + videoPlayerFragment.requireView().setLayoutParams(params); + + if (videoMetaFragment != null) { + FragmentTransaction transaction = fragmentManager.beginTransaction() + .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out); + + if (isLandscape) { + transaction.hide(videoMetaFragment); + } else { + transaction.show(videoMetaFragment); } - videoPlayerFragment.setIsFullscreen(true); - } else { - assert videoPlayerFragment != null; - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) Objects.requireNonNull(videoPlayerFragment.getView()).getLayoutParams(); - params.width = FrameLayout.LayoutParams.MATCH_PARENT; - params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 250, getResources().getDisplayMetrics()); - videoPlayerFragment.getView().setLayoutParams(params); - - if (videoMetaFragment != null) { - fragmentManager.beginTransaction() - .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out) - .show(videoMetaFragment) - .commit(); - } - videoPlayerFragment.setIsFullscreen(false); + transaction.commit(); } + videoPlayerFragment.setIsFullscreen(isLandscape); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } @Override protected void onDestroy() { - VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) getSupportFragmentManager().findFragmentById(R.id.video_player_fragment); assert videoPlayerFragment != null; videoPlayerFragment.destroyVideo(); - super.onDestroy(); Log.v(TAG, "onDestroy..."); } @@ -335,15 +326,6 @@ public class VideoPlayActivity extends AppCompatActivity { protected void onStop() { super.onStop(); -// SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); -// -// Log.v(TAG, "" + sharedPref.getBoolean(BACKGROUND_PLAY_PREF_KEY, false)); -// -// if (!sharedPref.getBoolean(BACKGROUND_PLAY_PREF_KEY, false)) { -// Log.v(TAG, "BACKGROUND_PLAY_PREF_KEY..."); -// stopService(new Intent(this, VideoPlayerService.class)); -// } - VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) getSupportFragmentManager().findFragmentById(R.id.video_player_fragment); @@ -362,108 +344,115 @@ public class VideoPlayActivity extends AppCompatActivity { @SuppressLint("NewApi") @Override - public void onUserLeaveHint () { + public void onUserLeaveHint() { + + Log.v(TAG, "onUserLeaveHint()..."); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); FragmentManager fragmentManager = getSupportFragmentManager(); VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment); - VideoMetaDataFragment videoMetaFragment = (VideoMetaDataFragment) fragmentManager.findFragmentById(R.id.video_meta_data_fragment); - String backgroundBehavior = sharedPref.getString("pref_background_behavior","backgroundStop"); - switch(backgroundBehavior){ - case "backgroundStop": - Log.v(TAG,"stop the video"); - videoPlayerFragment.pauseVideo(); - stopService(new Intent(this, VideoPlayerService.class)); - super.onBackPressed(); - break; - case "backgroundAudio": - Log.v(TAG,"play the Audio"); - super.onBackPressed(); - break; - case "backgroundFloat": - Log.v(TAG,"play in floating video"); - //canEnterPIPMode makes sure API level is high enough - if (canEnterPipMode(this)) { - Log.v(TAG, "enabling pip"); - enterPipMode(); - } else { - Log.v(TAG, "unable to use pip"); - } - break; + String backgroundBehavior = sharedPref.getString(getString(R.string.pref_background_behavior_key), getString(R.string.pref_background_stop_key)); + + assert videoPlayerFragment != null; + assert backgroundBehavior != null; + + if (backgroundBehavior.equals(getString(R.string.pref_background_stop_key))) { + Log.v(TAG, "stop the video"); + + videoPlayerFragment.pauseVideo(); + stopService(new Intent(this, VideoPlayerService.class)); + super.onBackPressed(); + + } else if (backgroundBehavior.equals(getString(R.string.pref_background_audio_key))) { + Log.v(TAG, "play the Audio"); + super.onBackPressed(); + + } else if (backgroundBehavior.equals(getString(R.string.pref_background_float_key))) { + Log.v(TAG, "play in floating video"); + //canEnterPIPMode makes sure API level is high enough + if (canEnterPipMode(this)) { + Log.v(TAG, "enabling pip"); + enterPipMode(); + } else { + Log.v(TAG, "unable to use pip"); + } + + } else { + // Deal with bad entries from older version + Log.v(TAG, "No setting, fallback"); + super.onBackPressed(); + } - Log.v(TAG, "onUserLeaveHint()..."); + + } - // @RequiresApi(api = Build.VERSION_CODES.O) + // @RequiresApi(api = Build.VERSION_CODES.O) @SuppressLint("NewApi") public void onBackPressed() { + Log.v(TAG, "onBackPressed()..."); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) getSupportFragmentManager().findFragmentById(R.id.video_player_fragment); - //copying Youtube behavior to have back button exit full screen. - if (videoPlayerFragment.getIsFullscreen()){ - Log.v(TAG,"exiting full screen"); + assert videoPlayerFragment != null; + + // copying Youtube behavior to have back button exit full screen. + if (videoPlayerFragment.getIsFullscreen()) { + Log.v(TAG, "exiting full screen"); videoPlayerFragment.fullScreenToggle(); return; } - - if (sharedPref.getBoolean("pref_back_pause", true)) { - assert videoPlayerFragment != null; + // pause video if pref is enabled + if (sharedPref.getBoolean(getString(R.string.pref_back_pause_key), true)) { videoPlayerFragment.pauseVideo(); } - String backgroundBehavior = sharedPref.getString("pref_background_behavior","backgroundStop"); + String backgroundBehavior = sharedPref.getString(getString(R.string.pref_background_behavior_key), getString(R.string.pref_background_stop_key)); + assert backgroundBehavior != null; - // Log.v(TAG,"backgroundBehavior: " + backgroundBehavior); + if (backgroundBehavior.equals(getString(R.string.pref_background_stop_key))) { + Log.v(TAG, "stop the video"); + videoPlayerFragment.pauseVideo(); + stopService(new Intent(this, VideoPlayerService.class)); + super.onBackPressed(); - switch (backgroundBehavior){ - case "backgroundStop": - Log.v(TAG,"stop the video"); - videoPlayerFragment.pauseVideo(); - stopService(new Intent(this, VideoPlayerService.class)); + } else if (backgroundBehavior.equals(getString(R.string.pref_background_audio_key))) { + Log.v(TAG, "play the Audio"); + super.onBackPressed(); + + } else if (backgroundBehavior.equals(getString(R.string.pref_background_float_key))) { + Log.v(TAG, "play in floating video"); + //canEnterPIPMode makes sure API level is high enough + if (canEnterPipMode(this)) { + Log.v(TAG, "enabling pip"); + enterPipMode(); + //fixes problem where back press doesn't bring up video list after returning from PIP mode + Intent intentSettings = new Intent(this, VideoListActivity.class); + this.startActivity(intentSettings); + } else { + Log.v(TAG, "Unable to enter PIP mode"); super.onBackPressed(); - break; - case "backgroundAudio": - Log.v(TAG,"play the Audio"); - super.onBackPressed(); - break; - case "backgroundFloat": - Log.v(TAG,"play in floating video"); - //canEnterPIPMode makes sure API level is high enough - if (canEnterPipMode(this)) { - Log.v(TAG, "enabling pip"); - enterPipMode(); - //fixes problem where back press doesn't bring up video list after returning from PIP mode - Intent intentSettings = new Intent(this, VideoListActivity.class); - this.startActivity(intentSettings); - } else { - Log.v(TAG,"Unable to enter PIP mode"); - super.onBackPressed(); - } - break; - default: - // Deal with bad entries from older version - Log.v(TAG,"No setting, fallback"); - super.onBackPressed(); - break; + } + + } else { + // Deal with bad entries from older version + Log.v(TAG, "No setting, fallback"); + super.onBackPressed(); + } - Log.v(TAG, "onBackPressed()..."); - } - public boolean canEnterPipMode(Context context) { - Log.v(TAG,"api version "+Build.VERSION.SDK_INT); - if (Build.VERSION.SDK_INT<28){ - return false; - } - AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return (AppOpsManager.MODE_ALLOWED== appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, android.os.Process.myUid(), context.getPackageName())); + + } + @RequiresApi(api = Build.VERSION_CODES.O) public void enterPipMode() { Rational rational = new Rational(239, 100); - Log.v(TAG,rational.toString()); + Log.v(TAG, rational.toString()); PictureInPictureParams mParams = new PictureInPictureParams.Builder() .setAspectRatio(rational) @@ -472,19 +461,26 @@ public class VideoPlayActivity extends AppCompatActivity { enterPictureInPictureMode(mParams); } + @Override - public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) { + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) { FragmentManager fragmentManager = getSupportFragmentManager(); VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment); - if (isInPictureInPictureMode) { - changedToPipMode(); - Log.v(TAG,"switched to pip "); - videoPlayerFragment.useController(false); + if (videoPlayerFragment != null) { + + if (isInPictureInPictureMode) { + changedToPipMode(); + Log.v(TAG, "switched to pip "); + videoPlayerFragment.useController(false); + } else { + changedToNormalMode(); + Log.v(TAG, "switched to normal"); + videoPlayerFragment.useController(true); + } + } else { - changedToNormalMode(); - Log.v(TAG,"switched to normal"); - videoPlayerFragment.useController(true); + Log.e(TAG, "videoPlayerFragment is NULL"); } } diff --git a/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java b/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java index f7225df..1e8ba2f 100644 --- a/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java +++ b/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java @@ -18,15 +18,11 @@ package net.schueller.peertube.adapter; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.provider.SearchRecentSuggestions; import android.text.TextUtils; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -39,12 +35,8 @@ import androidx.recyclerview.widget.RecyclerView; import net.schueller.peertube.R; -import net.schueller.peertube.activity.SelectServerActivity; -import net.schueller.peertube.activity.ServerAddressBookActivity; -import net.schueller.peertube.activity.VideoListActivity; import net.schueller.peertube.database.Server; import net.schueller.peertube.helper.APIUrlHelper; -import net.schueller.peertube.provider.SearchSuggestionsProvider; import net.schueller.peertube.service.LoginService; @@ -65,7 +57,7 @@ public class ServerListAdapter extends RecyclerView.Adapter { +public class ServerSearchAdapter extends RecyclerView.Adapter { private ArrayList serverList; - private SelectServerActivity activity; + private SearchServerActivity activity; private String baseUrl; - public ServerAdapter(ArrayList serverList, SelectServerActivity activity) { + public ServerSearchAdapter(ArrayList serverList, SearchServerActivity activity) { this.serverList = serverList; this.activity = activity; } @@ -57,7 +57,7 @@ public class ServerAdapter extends RecyclerView.Adapter { - Intent intentServer = new Intent(getActivity(), SelectServerActivity.class); + Intent intentServer = new Intent(getActivity(), SearchServerActivity.class); this.startActivityForResult(intentServer, PICK_SERVER); }); diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoMenuQualityFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoMenuQualityFragment.java index 45420d9..7865f91 100644 --- a/app/src/main/java/net/schueller/peertube/fragment/VideoMenuQualityFragment.java +++ b/app/src/main/java/net/schueller/peertube/fragment/VideoMenuQualityFragment.java @@ -17,6 +17,7 @@ */ package net.schueller.peertube.fragment; +import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; @@ -44,7 +45,7 @@ public class VideoMenuQualityFragment extends BottomSheetDialogFragment { public static final String TAG = "VideoMenuQuality"; private static File autoQualityFile; - public static VideoMenuQualityFragment newInstance(ArrayList files) { + public static VideoMenuQualityFragment newInstance(Context context, ArrayList files) { mFiles = files; @@ -53,7 +54,7 @@ public class VideoMenuQualityFragment extends BottomSheetDialogFragment { autoQualityFile = new File(); Resolution autoQualityResolution = new Resolution(); autoQualityResolution.setId(0); - autoQualityResolution.setLabel("Auto"); + autoQualityResolution.setLabel(context.getString(R.string.menu_video_options_quality_automated)); autoQualityFile.setId(0); autoQualityFile.setResolution(autoQualityResolution); } @@ -74,11 +75,11 @@ public class VideoMenuQualityFragment extends BottomSheetDialogFragment { false); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); - Integer videoQuality = sharedPref.getInt("pref_quality", 0); + Integer videoQuality = sharedPref.getInt(getString(R.string.pref_quality_key), 0); for (File file : mFiles) { - LinearLayout menuRow = (LinearLayout) inflater.inflate(R.layout.row_popup_menu, null); + LinearLayout menuRow = (LinearLayout) inflater.inflate(R.layout.row_popup_menu, container); TextView iconView = menuRow.findViewById(R.id.video_quality_icon); iconView.setId(file.getResolution().getId()); @@ -90,7 +91,7 @@ public class VideoMenuQualityFragment extends BottomSheetDialogFragment { textView.setOnClickListener(view1 -> { // Log.v(TAG, file.getResolution().getLabel()); SharedPreferences.Editor editor = sharedPref.edit(); - editor.putInt("pref_quality", file.getResolution().getId()); + editor.putInt(getString(R.string.pref_quality_key), file.getResolution().getId()); editor.apply(); for (File fileV : mFiles) { diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoMetaDataFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoMetaDataFragment.java index 0785eeb..1d370e0 100644 --- a/app/src/main/java/net/schueller/peertube/fragment/VideoMetaDataFragment.java +++ b/app/src/main/java/net/schueller/peertube/fragment/VideoMetaDataFragment.java @@ -65,6 +65,10 @@ public class VideoMetaDataFragment extends Fragment { private static final String TAG = "VideoMetaDataFragment"; + private static final String RATING_NONE = "none"; + private static final String RATING_LIKE = "like"; + private static final String RATING_DISLIKE = "dislike"; + private Rating videoRating; private ColorStateList defaultTextColor; @@ -72,13 +76,11 @@ public class VideoMetaDataFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_video_meta, container, false); } public void updateVideoMeta(Video video, VideoPlayerService mService) { - Context context = getContext(); Activity activity = getActivity(); @@ -91,26 +93,23 @@ public class VideoMetaDataFragment extends Fragment { thumbsUpButton.setText(R.string.video_thumbs_up_icon); new Iconics.IconicsBuilder().ctx(context).on(thumbsUpButton).build(); thumbsUpButton.setOnClickListener(v -> { - rateVideo(true, video.getId()); + rateVideo(true, video); }); - TextView thumbsUpButtonTotal = activity.findViewById(R.id.video_thumbs_up_total); - thumbsUpButtonTotal.setText(video.getLikes().toString()); - // Thumbs Down Button thumbsDownButton = activity.findViewById(R.id.video_thumbs_down); thumbsDownButton.setText(R.string.video_thumbs_down_icon); new Iconics.IconicsBuilder().ctx(context).on(thumbsDownButton).build(); thumbsDownButton.setOnClickListener(v -> { - rateVideo(false, video.getId()); + rateVideo(false, video); }); - // video rating videoRating = new Rating(); - videoRating.setRating("none"); // default - updateVideoRating(); + videoRating.setRating(RATING_NONE); // default + updateVideoRating(video); + // Retrieve which rating the user gave to this video if (Session.getInstance().isLoggedIn()) { Call call = videoDataService.getVideoRating(video.getId()); call.enqueue(new Callback() { @@ -118,20 +117,16 @@ public class VideoMetaDataFragment extends Fragment { @Override public void onResponse(Call call, Response response) { videoRating = response.body(); - updateVideoRating(); + updateVideoRating(video); } @Override public void onFailure(Call call, Throwable t) { -// Toast.makeText(context, "Rating Failed", Toast.LENGTH_SHORT).show(); + // Do nothing. } }); } - - TextView thumbsDownButtonTotal = activity.findViewById(R.id.video_thumbs_down_total); - thumbsDownButtonTotal.setText(video.getDislikes().toString()); - // Share Button videoShareButton = activity.findViewById(R.id.video_share); videoShareButton.setText(R.string.video_share_icon); @@ -156,7 +151,6 @@ public class VideoMetaDataFragment extends Fragment { } }); - Account account = video.getAccount(); // owner / creator Avatar @@ -198,7 +192,6 @@ public class VideoMetaDataFragment extends Fragment { TextView videoDescription = activity.findViewById(R.id.description); videoDescription.setText(video.getDescription()); - // video privacy TextView videoPrivacy = activity.findViewById(R.id.video_privacy); videoPrivacy.setText(video.getPrivacy().getLabel()); @@ -211,7 +204,7 @@ public class VideoMetaDataFragment extends Fragment { TextView videoLicense = activity.findViewById(R.id.video_license); videoLicense.setText(video.getLicence().getLabel()); - // video langauge + // video language TextView videoLanguage = activity.findViewById(R.id.video_language); videoLanguage.setText(video.getLanguage().getLabel()); @@ -219,7 +212,6 @@ public class VideoMetaDataFragment extends Fragment { TextView videoTags = activity.findViewById(R.id.video_tags); videoTags.setText(android.text.TextUtils.join(", ", video.getTags())); - // more button TextView moreButton = activity.findViewById(R.id.moreButton); moreButton.setText(R.string.video_more_icon); @@ -259,8 +251,7 @@ public class VideoMetaDataFragment extends Fragment { } - - void updateVideoRating() { + void updateVideoRating(Video video) { Button thumbsUpButton = getActivity().findViewById(R.id.video_thumbs_up); Button thumbsDownButton = getActivity().findViewById(R.id.video_thumbs_down); @@ -269,44 +260,46 @@ public class VideoMetaDataFragment extends Fragment { TypedArray a = getContext().obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimary}); int accentColor = a.getColor(0, 0); - if (videoRating.getRating().equals(getString(R.string.video_rating_none))) { - thumbsUpButton.setTextColor(defaultTextColor); - thumbsDownButton.setTextColor(defaultTextColor); - //Log.v(TAG, getString(R.string.video_rating_none)); - - } else if (videoRating.getRating().equals(getString(R.string.video_rating_like))) { - thumbsUpButton.setTextColor(accentColor); - thumbsDownButton.setTextColor(defaultTextColor); - //Log.v(TAG, getString(R.string.video_rating_like)); - - } else if (videoRating.getRating().equals(getString(R.string.video_rating_dislike))) { - thumbsUpButton.setTextColor(defaultTextColor); - thumbsDownButton.setTextColor(accentColor); - //Log.v(TAG, getString(R.string.video_rating_dislike)); - + // Change the color of the thumbs + switch (videoRating.getRating()) { + case RATING_NONE: + thumbsUpButton.setTextColor(defaultTextColor); + thumbsDownButton.setTextColor(defaultTextColor); + break; + case RATING_LIKE: + thumbsUpButton.setTextColor(accentColor); + thumbsDownButton.setTextColor(defaultTextColor); + break; + case RATING_DISLIKE: + thumbsUpButton.setTextColor(defaultTextColor); + thumbsDownButton.setTextColor(accentColor); + break; } + // Update the texts + TextView thumbsDownTotal = getActivity().findViewById(R.id.video_thumbs_down_total); + TextView thumbsUpTotal = getActivity().findViewById(R.id.video_thumbs_up_total); + thumbsUpTotal.setText(String.valueOf(video.getLikes())); + thumbsDownTotal.setText(String.valueOf(video.getDislikes())); + a.recycle(); } - void rateVideo(Boolean rate, Integer videoId) { - - // TODO cleanup - + void rateVideo(Boolean like, Video video) { if (Session.getInstance().isLoggedIn()) { + final String ratePayload; - String ratePayload = getString(R.string.video_rating_none); - - if (rate) { - // thumbsup - if (videoRating.getRating().equals(getString(R.string.video_rating_none))) { - ratePayload = getString(R.string.video_rating_like); - } - } else { - // thumbsdown - if (videoRating.getRating().equals(getString(R.string.video_rating_none))) { - ratePayload = getString(R.string.video_rating_dislike); - } + switch (videoRating.getRating()) { + case RATING_LIKE: + ratePayload = like ? RATING_NONE : RATING_DISLIKE; + break; + case RATING_DISLIKE: + ratePayload = like ? RATING_LIKE : RATING_NONE; + break; + case RATING_NONE: + default: + ratePayload = like ? RATING_LIKE : RATING_DISLIKE; + break; } RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"), "{\"rating\":\"" + ratePayload + "\"}"); @@ -314,23 +307,47 @@ public class VideoMetaDataFragment extends Fragment { String apiBaseURL = APIUrlHelper.getUrlWithVersion(getContext()); GetVideoDataService videoDataService = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); - Call call = videoDataService.rateVideo(videoId, body); - - final String newRating = ratePayload; + Call call = videoDataService.rateVideo(video.getId(), body); call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { - //Log.v(TAG, response.toString()); - // if 20x update likes + // if 20x, update likes/dislikes if (response.isSuccessful()) { - videoRating.setRating(newRating); - updateVideoRating(); + String previousRating = videoRating.getRating(); - // TODO: update count under thumb + // Update the likes/dislikes count of the video, if needed. + // This is only a visual trick, as the actual like/dislike count has + // already been modified on the PeerTube instance. + if (!previousRating.equals(ratePayload)) { + switch (previousRating) { + case RATING_NONE: + if (ratePayload.equals(RATING_LIKE)) { + video.setLikes(video.getLikes() + 1); + } else { + video.setDislikes(video.getDislikes() + 1); + } + break; + case RATING_LIKE: + video.setLikes(video.getLikes() - 1); + if (ratePayload.equals(RATING_DISLIKE)) { + video.setDislikes(video.getDislikes() + 1); + } + break; + case RATING_DISLIKE: + video.setDislikes(video.getDislikes() - 1); + if (ratePayload.equals(RATING_LIKE)) { + video.setLikes(video.getLikes() + 1); + } + break; + } + } + + videoRating.setRating(ratePayload); + updateVideoRating(video); } } @@ -341,9 +358,7 @@ public class VideoMetaDataFragment extends Fragment { }); } else { Toast.makeText(getContext(), getString(R.string.video_login_required_for_service), Toast.LENGTH_SHORT).show(); - } - } } diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java index 212c2d2..b0fb6d2 100644 --- a/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java +++ b/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java @@ -17,9 +17,9 @@ */ package net.schueller.peertube.fragment; -import android.annotation.SuppressLint; +import android.content.SharedPreferences; import android.os.Bundle; -import android.util.Log; +import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,6 +28,7 @@ import android.widget.TextView; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.mikepenz.iconics.Iconics; + import net.schueller.peertube.R; import net.schueller.peertube.model.File; import net.schueller.peertube.service.VideoPlayerService; @@ -35,6 +36,7 @@ import net.schueller.peertube.service.VideoPlayerService; import java.util.ArrayList; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; public class VideoOptionsFragment extends BottomSheetDialogFragment { @@ -62,32 +64,41 @@ public class VideoOptionsFragment extends BottomSheetDialogFragment { LinearLayout menuHolder = view.findViewById(R.id.video_options_popup); // Video Speed - LinearLayout menuRow = (LinearLayout) inflater.inflate(R.layout.row_popup_menu, null); + LinearLayout menuRow = (LinearLayout) inflater.inflate(R.layout.row_popup_menu, container); TextView iconView = menuRow.findViewById(R.id.video_quality_icon); TextView textView = menuRow.findViewById(R.id.video_quality_text); - textView.setText(getString(R.string.menu_video_options_playback_speed)); + + textView.setText( + getString( + R.string.menu_video_options_playback_speed, + getCurrentVideoPlaybackSpeedString(videoPlayerService.getPlayBackSpeed() + ) + ) + ); + + iconView.setText(R.string.video_option_speed_icon); new Iconics.IconicsBuilder().ctx(getContext()).on(iconView).build(); textView.setOnClickListener(view1 -> { VideoMenuSpeedFragment videoMenuSpeedFragment = VideoMenuSpeedFragment.newInstance(videoPlayerService); - videoMenuSpeedFragment.show(getActivity().getSupportFragmentManager(), + videoMenuSpeedFragment.show(requireActivity().getSupportFragmentManager(), VideoMenuSpeedFragment.TAG); }); menuHolder.addView(menuRow); // Video Quality - LinearLayout menuRow2 = (LinearLayout) inflater.inflate(R.layout.row_popup_menu, null); + LinearLayout menuRow2 = (LinearLayout) inflater.inflate(R.layout.row_popup_menu, container); TextView iconView2 = menuRow2.findViewById(R.id.video_quality_icon); TextView textView2 = menuRow2.findViewById(R.id.video_quality_text); - textView2.setText(getString(R.string.menu_video_options_quality)); + textView2.setText(String.format(getString(R.string.menu_video_options_quality), getCurrentVideoQuality(files))); iconView2.setText(R.string.video_option_quality_icon); new Iconics.IconicsBuilder().ctx(getContext()).on(iconView2).build(); textView2.setOnClickListener(view1 -> { VideoMenuQualityFragment videoMenuQualityFragment = - VideoMenuQualityFragment.newInstance(files); - videoMenuQualityFragment.show(getActivity().getSupportFragmentManager(), - videoMenuQualityFragment.TAG); + VideoMenuQualityFragment.newInstance(getContext(), files); + videoMenuQualityFragment.show(requireActivity().getSupportFragmentManager(), + VideoMenuQualityFragment.TAG); }); menuHolder.addView(menuRow2); @@ -95,4 +106,26 @@ public class VideoOptionsFragment extends BottomSheetDialogFragment { } + private String getCurrentVideoQuality(ArrayList files) { + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); + Integer videoQuality = sharedPref.getInt(getString(R.string.pref_quality_key), 0); + + for (File file : files) { + if (videoQuality.equals(file.getResolution().getId())) { + return file.getResolution().getLabel(); + } + } + // Returning Automated as a placeholder + return getString(R.string.menu_video_options_quality_automated); + } + + private String getCurrentVideoPlaybackSpeedString(float playbackSpeed) { + String speed = String.valueOf(playbackSpeed); + // Remove all non-digit characters from the string + speed = speed.replaceAll("[^0-9]", ""); + + // Dynamically get the localized string corresponding to the speed + @StringRes int stringId = getResources().getIdentifier("video_speed_" + speed, "string", videoPlayerService.getPackageName()); + return getString(stringId); + } } \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java index 9fad3b3..3ac32ba 100644 --- a/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java +++ b/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java @@ -18,7 +18,6 @@ package net.schueller.peertube.fragment; import android.app.Activity; -import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -66,15 +65,16 @@ import net.schueller.peertube.network.GetVideoDataService; import net.schueller.peertube.network.RetrofitInstance; import net.schueller.peertube.service.VideoPlayerService; -import java.util.Objects; - import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.fragment.app.Fragment; + import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +import static net.schueller.peertube.helper.VideoHelper.canEnterPipMode; + public class VideoPlayerFragment extends Fragment implements VideoRendererEventListener { private String mVideoUuid; @@ -134,6 +134,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL progressBar = activity.findViewById(R.id.torrent_progress); progressBar.setMax(100); + assert context != null; simpleExoPlayerView = new PlayerView(context); simpleExoPlayerView = activity.findViewById(R.id.video_view); @@ -195,30 +196,31 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL @Override public void onFailure(@NonNull Call