diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 434056b20..ee34f01dd 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -15,6 +15,7 @@ import android.widget.Toast; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.preference.Preference; @@ -230,8 +231,11 @@ public class ContentSettingsFragment extends BasePreferenceFragment { }) .setPositiveButton(R.string.ok, (dialog, which) -> { dialog.dismiss(); - manager.loadSharedPreferences(PreferenceManager - .getDefaultSharedPreferences(requireContext())); + final Context context = requireContext(); + final SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + manager.loadSharedPreferences(prefs); + cleanImport(context, prefs); finishImport(importDataUri); }) .show(); @@ -243,6 +247,38 @@ public class ContentSettingsFragment extends BasePreferenceFragment { } } + /** + * Remove settings that are not supposed to be imported on different devices + * and reset them to default values. + * @param context the context used for the import + * @param prefs the preferences used while running the import + */ + private void cleanImport(@NonNull final Context context, + @NonNull final SharedPreferences prefs) { + // Check if media tunnelling needs to be disabled automatically, + // if it was disabled automatically in the imported preferences. + final String tunnelingKey = context.getString(R.string.disable_media_tunneling_key); + final String automaticTunnelingKey = + context.getString(R.string.disabled_media_tunneling_automatically_key); + // R.string.disable_media_tunneling_key should always be true + // if R.string.disabled_media_tunneling_automatically_key equals 1, + // but we double check here just to be sure and to avoid regressions + // caused by possible later modification of the media tunneling functionality. + // R.string.disabled_media_tunneling_automatically_key == 0: + // automatic value overridden by user in settings + // R.string.disabled_media_tunneling_automatically_key == -1: not set + final boolean wasMediaTunnelingDisabledAutomatically = + prefs.getInt(automaticTunnelingKey, -1) == 1 + && prefs.getBoolean(tunnelingKey, false); + if (wasMediaTunnelingDisabledAutomatically) { + prefs.edit() + .putInt(automaticTunnelingKey, -1) + .putBoolean(tunnelingKey, false) + .apply(); + NewPipeSettings.setMediaTunneling(context); + } + } + /** * Save import path and restart system. * diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt index 8adf6a649..92be93a29 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt @@ -67,6 +67,9 @@ class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) { return ZipHelper.extractFileFromZip(file, fileLocator.settings.path, "newpipe.settings") } + /** + * Remove all shared preferences from the app and load the preferences supplied to the manager. + */ fun loadSharedPreferences(preferences: SharedPreferences) { try { val preferenceEditor = preferences.edit() diff --git a/app/src/main/java/org/schabi/newpipe/settings/ExoPlayerSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ExoPlayerSettingsFragment.java index 7e740f869..14dd0c409 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ExoPlayerSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ExoPlayerSettingsFragment.java @@ -1,8 +1,14 @@ package org.schabi.newpipe.settings; +import android.content.SharedPreferences; import android.os.Bundle; import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; +import androidx.preference.SwitchPreferenceCompat; + +import org.schabi.newpipe.R; public class ExoPlayerSettingsFragment extends BasePreferenceFragment { @@ -10,5 +16,30 @@ public class ExoPlayerSettingsFragment extends BasePreferenceFragment { public void onCreatePreferences(@Nullable final Bundle savedInstanceState, @Nullable final String rootKey) { addPreferencesFromResourceRegistry(); + + final String disabledMediaTunnelingAutomaticallyKey = + getString(R.string.disabled_media_tunneling_automatically_key); + final SwitchPreferenceCompat disableMediaTunnelingPref = + (SwitchPreferenceCompat) requirePreference(R.string.disable_media_tunneling_key); + final SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(requireContext()); + final boolean mediaTunnelingAutomaticallyDisabled = + prefs.getInt(disabledMediaTunnelingAutomaticallyKey, -1) == 1; + final String summaryText = getString(R.string.disable_media_tunneling_summary); + disableMediaTunnelingPref.setSummary(mediaTunnelingAutomaticallyDisabled + ? summaryText + " " + getString(R.string.disable_media_tunneling_automatic_info) + : summaryText); + + disableMediaTunnelingPref.setOnPreferenceChangeListener((Preference p, Object enabled) -> { + if (Boolean.FALSE.equals(enabled)) { + PreferenceManager.getDefaultSharedPreferences(requireContext()) + .edit() + .putInt(disabledMediaTunnelingAutomaticallyKey, 0) + .apply(); + // the info text might have been shown before + p.setSummary(R.string.disable_media_tunneling_summary); + } + return true; + }); } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 16df646f9..2cca08072 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.settings; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + import android.content.Context; import android.content.SharedPreferences; import android.os.Build; @@ -15,8 +17,6 @@ import org.schabi.newpipe.util.DeviceUtils; import java.io.File; import java.util.Set; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; - /* * Created by k3b on 07.01.2016. * @@ -61,7 +61,7 @@ public final class NewPipeSettings { } // first run migrations, then setDefaultValues, since the latter requires the correct types - SettingMigrations.initMigrations(context, isFirstRun); + SettingMigrations.runMigrationsIfNeeded(context, isFirstRun); // readAgain is true so that if new settings are added their default value is set PreferenceManager.setDefaultValues(context, R.xml.main_settings, true); @@ -76,6 +76,8 @@ public final class NewPipeSettings { saveDefaultVideoDownloadDirectory(context); saveDefaultAudioDownloadDirectory(context); + + disableMediaTunnelingIfNecessary(context, isFirstRun); } static void saveDefaultVideoDownloadDirectory(final Context context) { @@ -152,4 +154,49 @@ public final class NewPipeSettings { return showSearchSuggestions(context, sharedPreferences, R.string.show_remote_search_suggestions_key); } + + private static void disableMediaTunnelingIfNecessary(@NonNull final Context context, + final boolean isFirstRun) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + final String disabledTunnelingKey = context.getString(R.string.disable_media_tunneling_key); + final String disabledTunnelingAutomaticallyKey = + context.getString(R.string.disabled_media_tunneling_automatically_key); + final String blacklistVersionKey = + context.getString(R.string.media_tunneling_device_blacklist_version); + + final int lastMediaTunnelingUpdate = prefs.getInt(blacklistVersionKey, 0); + final boolean wasDeviceBlacklistUpdated = + DeviceUtils.MEDIA_TUNNELING_DEVICE_BLACKLIST_VERSION != lastMediaTunnelingUpdate; + final boolean wasMediaTunnelingEnabledByUser = + prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0 + && !prefs.getBoolean(disabledTunnelingKey, false); + + if (Boolean.TRUE.equals(isFirstRun) + || (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) { + setMediaTunneling(context); + } + } + + /** + * Check if device does not support media tunneling + * and disable that exoplayer feature if necessary. + * @see DeviceUtils#shouldSupportMediaTunneling() + * @param context + */ + public static void setMediaTunneling(@NonNull final Context context) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + if (!DeviceUtils.shouldSupportMediaTunneling()) { + prefs.edit() + .putBoolean(context.getString(R.string.disable_media_tunneling_key), true) + .putInt(context.getString( + R.string.disabled_media_tunneling_automatically_key), 1) + .putInt(context.getString(R.string.media_tunneling_device_blacklist_version), + DeviceUtils.MEDIA_TUNNELING_DEVICE_BLACKLIST_VERSION) + .apply(); + } else { + prefs.edit() + .putInt(context.getString(R.string.media_tunneling_device_blacklist_version), + DeviceUtils.MEDIA_TUNNELING_DEVICE_BLACKLIST_VERSION).apply(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index 1bfaec6c2..215caaa38 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; +import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; @@ -30,9 +31,9 @@ public final class SettingMigrations { private static final String TAG = SettingMigrations.class.toString(); private static SharedPreferences sp; - public static final Migration MIGRATION_0_1 = new Migration(0, 1) { + private static final Migration MIGRATION_0_1 = new Migration(0, 1) { @Override - public void migrate(final Context context) { + public void migrate(@NonNull final Context context) { // We changed the content of the dialog which opens when sharing a link to NewPipe // by removing the "open detail page" option. // Therefore, show the dialog once again to ensure users need to choose again and are @@ -44,9 +45,9 @@ public final class SettingMigrations { } }; - public static final Migration MIGRATION_1_2 = new Migration(1, 2) { + private static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override - protected void migrate(final Context context) { + protected void migrate(@NonNull final Context context) { // The new application workflow introduced in #2907 allows minimizing videos // while playing to do other stuff within the app. // For an even better workflow, we minimize a stream when switching the app to play in @@ -63,9 +64,9 @@ public final class SettingMigrations { } }; - public static final Migration MIGRATION_2_3 = new Migration(2, 3) { + private static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override - protected void migrate(final Context context) { + protected void migrate(@NonNull final Context context) { // Storage Access Framework implementation was improved in #5415, allowing the modern // and standard way to access folders and files to be used consistently everywhere. // We reset the setting to its default value, i.e. "use SAF", since now there are no @@ -79,9 +80,9 @@ public final class SettingMigrations { } }; - public static final Migration MIGRATION_3_4 = new Migration(3, 4) { + private static final Migration MIGRATION_3_4 = new Migration(3, 4) { @Override - protected void migrate(final Context context) { + protected void migrate(@NonNull final Context context) { // Pull request #3546 added support for choosing the type of search suggestions to // show, replacing the on-off switch used before, so migrate the previous user choice @@ -108,9 +109,9 @@ public final class SettingMigrations { } }; - public static final Migration MIGRATION_4_5 = new Migration(4, 5) { + private static final Migration MIGRATION_4_5 = new Migration(4, 5) { @Override - protected void migrate(final Context context) { + protected void migrate(@NonNull final Context context) { final boolean brightness = sp.getBoolean("brightness_gesture_control", true); final boolean volume = sp.getBoolean("volume_gesture_control", true); @@ -144,10 +145,11 @@ public final class SettingMigrations { /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - public static final int VERSION = 5; + private static final int VERSION = 5; - public static void initMigrations(final Context context, final boolean isFirstRun) { + public static void runMigrationsIfNeeded(@NonNull final Context context, + final boolean isFirstRun) { // setup migrations and check if there is something to do sp = PreferenceManager.getDefaultSharedPreferences(context); final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); @@ -212,7 +214,7 @@ public final class SettingMigrations { return oldVersion >= currentVersion; } - protected abstract void migrate(Context context); + protected abstract void migrate(@NonNull Context context); } diff --git a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java index f656c6144..e9678c2b0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java @@ -36,6 +36,91 @@ public final class DeviceUtils { private static Boolean isTV = null; private static Boolean isFireTV = null; + /** + *

The app version code that corresponds to the last update + * of the media tunneling device blacklist.

+ *

The value of this variable needs to be updated everytime a new device that does not + * support media tunneling to match the upcoming version code.

+ * @see #shouldSupportMediaTunneling() + */ + public static final int MEDIA_TUNNELING_DEVICE_BLACKLIST_VERSION = 994; + + // region: devices not supporting media tunneling / media tunneling blacklist + /** + *

Formuler Z8 Pro, Z8, CC, Z Alpha, Z+ Neo.

+ *

Blacklist reason: black screen

+ *

Board: HiSilicon Hi3798MV200

+ */ + private static final boolean HI3798MV200 = Build.VERSION.SDK_INT == 24 + && Build.DEVICE.equals("Hi3798MV200"); + /** + *

Zephir TS43UHD-2.

+ *

Blacklist reason: black screen

+ */ + private static final boolean CVT_MT5886_EU_1G = Build.VERSION.SDK_INT == 24 + && Build.DEVICE.equals("cvt_mt5886_eu_1g"); + /** + * Hilife TV. + *

Blacklist reason: black screen

+ */ + private static final boolean REALTEKATV = Build.VERSION.SDK_INT == 25 + && Build.DEVICE.equals("RealtekATV"); + /** + *

Phillips 4K (O)LED TV.

+ * Supports custom ROMs with different API levels + */ + private static final boolean PH7M_EU_5596 = Build.VERSION.SDK_INT >= 26 + && Build.DEVICE.equals("PH7M_EU_5596"); + /** + *

Philips QM16XE.

+ *

Blacklist reason: black screen

+ */ + private static final boolean QM16XE_U = Build.VERSION.SDK_INT == 23 + && Build.DEVICE.equals("QM16XE_U"); + /** + *

Sony Bravia VH1.

+ *

Processor: MT5895

+ *

Blacklist reason: fullscreen crash / stuttering

+ */ + private static final boolean BRAVIA_VH1 = Build.VERSION.SDK_INT == 29 + && Build.DEVICE.equals("BRAVIA_VH1"); + /** + *

Sony Bravia VH2.

+ *

Blacklist reason: fullscreen crash; this includes model A90J as reported in + * + * #9023

+ */ + private static final boolean BRAVIA_VH2 = Build.VERSION.SDK_INT == 29 + && Build.DEVICE.equals("BRAVIA_VH2"); + /** + *

Sony Bravia Android TV platform 2.

+ * Uses a MediaTek MT5891 (MT5596) SoC. + * @see + * https://github.com/CiNcH83/bravia_atv2 + */ + private static final boolean BRAVIA_ATV2 = Build.DEVICE.equals("BRAVIA_ATV2"); + /** + *

Sony Bravia Android TV platform 3 4K.

+ *

Uses ARM MT5891 and a {@link #BRAVIA_ATV2} motherboard.

+ * + * @see + * https://browser.geekbench.com/v4/cpu/9101105 + */ + private static final boolean BRAVIA_ATV3_4K = Build.DEVICE.equals("BRAVIA_ATV3_4K"); + /** + *

Panasonic 4KTV-JUP.

+ *

Blacklist reason: fullscreen crash

+ */ + private static final boolean TX_50JXW834 = Build.DEVICE.equals("TX_50JXW834"); + /** + *

Bouygtel4K / Bouygues Telecom Bbox 4K.

+ *

Blacklist reason: black screen; reported at + * + * #10122

+ */ + private static final boolean HMB9213NW = Build.DEVICE.equals("HMB9213NW"); + // endregion + private DeviceUtils() { } @@ -224,4 +309,30 @@ public final class DeviceUtils { return point.y; } } + + /** + *

Some devices have broken tunneled video playback but claim to support it.

+ *

This can cause a black video player surface while attempting to play a video or + * crashes while entering or exiting the full screen player. + * The issue effects Android TVs most commonly. + * See #5911 and + * #9023 for more info.

+ * @Note Update {@link #MEDIA_TUNNELING_DEVICE_BLACKLIST_VERSION} + * when adding a new device to the method. + * @return {@code false} if affected device; {@code true} otherwise + */ + public static boolean shouldSupportMediaTunneling() { + // Maintainers note: update MEDIA_TUNNELING_DEVICES_UPDATE_APP_VERSION_CODE + return !HI3798MV200 + && !CVT_MT5886_EU_1G + && !REALTEKATV + && !QM16XE_U + && !BRAVIA_VH1 + && !BRAVIA_VH2 + && !BRAVIA_ATV2 + && !BRAVIA_ATV3_4K + && !PH7M_EU_5596 + && !TX_50JXW834 + && !HMB9213NW; + } } diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index eebe29d81..a41d41fdb 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -1382,6 +1382,8 @@ exoplayer_settings_key disable_media_tunneling_key + disabled_media_tunneling_automatically_key + media_tunneling_device_blacklist_version use_exoplayer_decoder_fallback_key always_use_exoplayer_set_output_surface_workaround_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 75acedf9e..e4fcdeedc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -480,7 +480,8 @@ Show original time ago on items Original texts from services will be visible in stream items Disable media tunneling - Disable media tunneling if you experience a black screen or stuttering on video playback + Disable media tunneling if you experience a black screen or stuttering on video playback. + Media tunneling was disabled by default on your device because your device model is known to not support it. Show image indicators Show Picasso colored ribbons on top of images indicating their source: red for network, blue for disk and green for memory Show \"Crash the player\"