From 89645fab998f62b8d9d3a0e2adb5126adf4f87ff Mon Sep 17 00:00:00 2001 From: Siddhesh Naik Date: Tue, 27 Feb 2024 03:03:48 +0530 Subject: [PATCH 1/2] Initial setup for Settings page redesign with debug settings - Added a new settings option to enable the settings page redesign. - This option allows us to integrate and test the new settings page gradually, minimizing disruptions to the current behavior. - Reused the rest of the debug menu to reflect in the new settings page. - Verified the functionality of all debug settings. Next plan: - Create PRs to gradually add all the subsections as detailed in https://github.com/TeamNewPipe/NewPipe/issues/9587. Note: - This PR prepares for upcoming updates by setting up the necessary changes for follow-up PRs. --- .../settings/MainSettingsV2Fragment.kt | 49 +++++++++++++++++++ .../newpipe/settings/SettingsActivity.java | 9 +++- .../settings/SettingsResourceRegistry.java | 4 ++ app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/debug_settings.xml | 7 +++ app/src/main/res/xml/main_settings_v2.xml | 12 +++++ 7 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/MainSettingsV2Fragment.kt create mode 100644 app/src/main/res/xml/main_settings_v2.xml diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsV2Fragment.kt b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsV2Fragment.kt new file mode 100644 index 00000000000..1da3d565a22 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsV2Fragment.kt @@ -0,0 +1,49 @@ +package org.schabi.newpipe.settings + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import androidx.activity.ComponentActivity +import androidx.core.view.MenuProvider +import androidx.preference.Preference +import org.schabi.newpipe.R + +/** + * Provides main settings page, entry point to the NewPipe app settings. + */ +class MainSettingsV2Fragment : BasePreferenceFragment(), MenuProvider { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResourceRegistry() + + (activity as? ComponentActivity)?.addMenuProvider(this, this) + + // Hide debug preferences in RELEASE build variant + if (!DEBUG) { + findPreference(getString(R.string.debug_pref_screen_key))?.let( + preferenceScreen::removePreference, + ) + } + } + + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // -- Link settings activity and register menu -- + menuInflater.inflate(R.menu.menu_settings_main_fragment, menu) + val menuSearchItem = menu.getItem(0) + (activity as? SettingsActivity)?.setMenuSearchItem(menuSearchItem) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + if (menuItem.itemId == R.id.action_search) { + (activity as? SettingsActivity)?.setSearchActive(true) + return true + } + return false + } + + override fun onDestroy() { + // Unlink activity so that we don't get memory problems + (activity as? SettingsActivity)?.setMenuSearchItem(null) + super.onDestroy() + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java index 3ee6668bf94..c04e529848a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java @@ -20,6 +20,7 @@ import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; import com.jakewharton.rxbinding4.widget.RxTextView; @@ -112,8 +113,14 @@ protected void onCreate(final Bundle savedInstanceBundle) { } } } else { + final boolean shouldDisplaySettingsRedesign = PreferenceManager + .getDefaultSharedPreferences(this) + .getBoolean(getString(R.string.settings_layout_redesign_key), false); + final BasePreferenceFragment mainSettingsFragment = + shouldDisplaySettingsRedesign + ? new MainSettingsV2Fragment() : new MainSettingsFragment(); getSupportFragmentManager().beginTransaction() - .replace(R.id.settings_fragment_holder, new MainSettingsFragment()) + .replace(R.id.settings_fragment_holder, mainSettingsFragment) .commit(); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java index b3d0741bb44..328512d3e1c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java @@ -29,6 +29,10 @@ public final class SettingsResourceRegistry { private final Set registeredEntries = new HashSet<>(); private SettingsResourceRegistry() { + add(MainSettingsV2Fragment.class, R.xml.main_settings_v2).setSearchable(false); + + // Before redesign settings arrangement. These should be cleared once the + // settings_layout_redesign_key is approved and enabled by default. add(MainSettingsFragment.class, R.xml.main_settings).setSearchable(false); add(AppearanceSettingsFragment.class, R.xml.appearance_settings); diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 880fa92da7b..3c89757d176 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -245,6 +245,7 @@ crash_the_app_key show_error_snackbar_key create_error_notification_key + settings_layout_redesign_key theme diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 281df95a4b2..6fdba4045d6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -491,6 +491,7 @@ Crash the app Show an error snackbar Create an error notification + Enable the Redesigned Settings page Import Import from diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index 84bb281f31e..87a33adf2de 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -71,4 +71,11 @@ android:title="@string/create_error_notification" app:singleLineTitle="false" app:iconSpaceReserved="false" /> + + diff --git a/app/src/main/res/xml/main_settings_v2.xml b/app/src/main/res/xml/main_settings_v2.xml new file mode 100644 index 00000000000..3fd7a5a3ae1 --- /dev/null +++ b/app/src/main/res/xml/main_settings_v2.xml @@ -0,0 +1,12 @@ + + + + + From 5ca85861e70cdfa649c2890f314de78b910885f8 Mon Sep 17 00:00:00 2001 From: Siddhesh Naik Date: Tue, 27 Feb 2024 03:40:53 +0530 Subject: [PATCH 2/2] Implementation of player settings page in Settings page redesign - Added new files for the Fragment and xml related to the player settings page. - Implemented the player settings page per the description in https://github.com/TeamNewPipe/NewPipe/issues/9587. - These changes facilitate easier switching between current and new behaviors. - They also lay the groundwork for easier cleanup when the new design is approved and enabled by default. Video: - https://github.com/TeamNewPipe/NewPipe/assets/87667048/00385bbb-6a2f-4ce5-b6b9-6cf1cb5c5636 --- .../settings/PlayerSettingsFragment.kt | 211 ++++++++++++++++++ .../settings/SettingsResourceRegistry.java | 2 +- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/main_settings_v2.xml | 5 + app/src/main/res/xml/player_settings.xml | 150 +++++++++++++ 5 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/PlayerSettingsFragment.kt create mode 100644 app/src/main/res/xml/player_settings.xml diff --git a/app/src/main/java/org/schabi/newpipe/settings/PlayerSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/PlayerSettingsFragment.kt new file mode 100644 index 00000000000..dfc3c9827f0 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/PlayerSettingsFragment.kt @@ -0,0 +1,211 @@ +package org.schabi.newpipe.settings + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.content.res.Resources.NotFoundException +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import android.text.format.DateUtils +import android.widget.Toast +import androidx.preference.ListPreference +import androidx.preference.Preference +import com.google.android.material.snackbar.Snackbar +import org.schabi.newpipe.R +import org.schabi.newpipe.util.ListHelper +import org.schabi.newpipe.util.PermissionHelper +import java.util.LinkedList + +class PlayerSettingsFragment : BasePreferenceFragment() { + private lateinit var listener: OnSharedPreferenceChangeListener + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResourceRegistry() + updateSeekOptions() + updateResolutionOptions() + listener = OnSharedPreferenceChangeListener { sharedPreferences, key -> + // on M and above, if user chooses to minimise to popup player on exit and the app + // doesn't have display over other apps permission, show a snack bar to let the user + // give permission + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + getString(R.string.minimize_on_exit_key) == key + ) { + sharedPreferences.getString(key, null) + ?.takeIf { + getString(R.string.minimize_on_exit_popup_key) == it && + !Settings.canDrawOverlays(context) + }?.let { + Snackbar.make( + listView, + R.string.permission_display_over_apps, + Snackbar.LENGTH_INDEFINITE, + ).setAction(R.string.settings) { + PermissionHelper.checkSystemAlertWindowPermission(context) + }.show() + } + } else if (getString(R.string.use_inexact_seek_key) == key) { + updateSeekOptions() + } else if (getString(R.string.show_higher_resolutions_key) == key) { + updateResolutionOptions() + } + } + } + + /** + * Update default resolution, default popup resolution & mobile data resolution options. + *

+ * Show high resolutions when "Show higher resolution" option is enabled. + * Set default resolution to "best resolution" when "Show higher resolution" option + * is disabled. + */ + private fun updateResolutionOptions() { + val resources = resources + val showHigherResolutions = preferenceManager.getSharedPreferences() + ?.getBoolean(resources.getString(R.string.show_higher_resolutions_key), false) ?: false + + // get sorted resolution lists + val resolutionListDescriptions = ListHelper.getSortedResolutionList( + resources, + R.array.resolution_list_description, + R.array.high_resolution_list_descriptions, + showHigherResolutions, + ) + val resolutionListValues = ListHelper.getSortedResolutionList( + resources, + R.array.resolution_list_values, + R.array.high_resolution_list_values, + showHigherResolutions, + ) + val limitDataUsageResolutionValues = ListHelper.getSortedResolutionList( + resources, + R.array.limit_data_usage_values_list, + R.array.high_resolution_limit_data_usage_values_list, + showHigherResolutions, + ) + val limitDataUsageResolutionDescriptions = ListHelper + .getSortedResolutionList( + resources, + R.array.limit_data_usage_description_list, + R.array.high_resolution_list_descriptions, + showHigherResolutions, + ) + + // get resolution preferences + val defaultResolution = findPreference( + getString(R.string.default_resolution_key) + ) + val defaultPopupResolution = findPreference( + getString(R.string.default_popup_resolution_key) + ) + val mobileDataResolution = findPreference( + getString(R.string.limit_mobile_data_usage_key) + ) + + // update resolution preferences with new resolutions, entries & values for each + defaultResolution?.entries = resolutionListDescriptions.toTypedArray() + defaultResolution?.entryValues = resolutionListValues.toTypedArray() + defaultPopupResolution?.entries = resolutionListDescriptions.toTypedArray() + defaultPopupResolution?.entryValues = resolutionListValues.toTypedArray() + mobileDataResolution?.entries = limitDataUsageResolutionDescriptions.toTypedArray() + mobileDataResolution?.entryValues = + limitDataUsageResolutionValues.toTypedArray() + + // if "Show higher resolution" option is disabled, + // set default resolution to "best resolution" + if (!showHigherResolutions) { + if (defaultResolution != null && ListHelper.isHighResolutionSelected( + defaultResolution.value, + R.array.high_resolution_list_values, + resources + ) + ) { + defaultResolution.setValueIndex(0) + } + if (defaultPopupResolution != null && ListHelper.isHighResolutionSelected( + defaultPopupResolution.value, + R.array.high_resolution_list_values, + resources, + ) + ) { + defaultPopupResolution.setValueIndex(0) + } + if (mobileDataResolution != null && ListHelper.isHighResolutionSelected( + mobileDataResolution.value, + R.array.high_resolution_limit_data_usage_values_list, + resources, + ) + ) { + mobileDataResolution.setValueIndex(0) + } + } + } + + /** + * Update fast-forward/-rewind seek duration options + * according to language and inexact seek setting. + * Exoplayer can't seek 5 seconds in audio when using inexact seek. + */ + private fun updateSeekOptions() { + // initializing R.array.seek_duration_description to display the translation of seconds + val durationsValues = resources.getStringArray(R.array.seek_duration_value) + val displayedDurationValues = LinkedList() + val displayedDescriptionValues = LinkedList() + val inexactSeek = preferenceManager.getSharedPreferences() + ?.getBoolean(resources.getString(R.string.use_inexact_seek_key), false) ?: false + for (durationsValue in durationsValues) { + val currentDurationValue = durationsValue.toInt() / DateUtils.SECOND_IN_MILLIS.toInt() + if (inexactSeek && currentDurationValue % 10 == 5) { + continue + } + displayedDurationValues.add(durationsValue) + try { + displayedDescriptionValues.add( + String.format( + resources.getQuantityString(R.plurals.seconds, currentDurationValue), + currentDurationValue + ) + ) + } catch (ignored: NotFoundException) { + // if this happens, the translation is missing, + // and the english string will be displayed instead + } + } + findPreference(getString(R.string.seek_duration_key))?.apply { + entryValues = displayedDurationValues.toTypedArray() + entries = displayedDescriptionValues.toTypedArray() + val selectedDuration = value.toLong() + if (inexactSeek && selectedDuration / DateUtils.SECOND_IN_MILLIS % 10 == 5L) { + val newDuration = selectedDuration / DateUtils.SECOND_IN_MILLIS + 5 + value = (newDuration * DateUtils.SECOND_IN_MILLIS).toString() + Toast.makeText( + context, + getString(R.string.new_seek_duration_toast, newDuration), + Toast.LENGTH_LONG, + ).show() + } + } + } + + override fun onResume() { + super.onResume() + preferenceManager.getSharedPreferences()?.registerOnSharedPreferenceChangeListener(listener) + } + + override fun onPause() { + super.onPause() + preferenceManager.getSharedPreferences()?.unregisterOnSharedPreferenceChangeListener(listener) + } + + override fun onPreferenceTreeClick(preference: Preference): Boolean { + if (getString(R.string.caption_settings_key) == preference.key) { + try { + startActivity(Intent(Settings.ACTION_CAPTIONING_SETTINGS)) + } catch (error: ActivityNotFoundException) { + Toast.makeText(requireContext(), R.string.general_error, Toast.LENGTH_SHORT).show() + } + } + + return super.onPreferenceTreeClick(preference) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java index 328512d3e1c..77adb7dc96b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java @@ -30,11 +30,11 @@ public final class SettingsResourceRegistry { private SettingsResourceRegistry() { add(MainSettingsV2Fragment.class, R.xml.main_settings_v2).setSearchable(false); + add(PlayerSettingsFragment.class, R.xml.player_settings); // Before redesign settings arrangement. These should be cleared once the // settings_layout_redesign_key is approved and enabled by default. add(MainSettingsFragment.class, R.xml.main_settings).setSearchable(false); - add(AppearanceSettingsFragment.class, R.xml.appearance_settings); add(ContentSettingsFragment.class, R.xml.content_settings); add(DebugSettingsFragment.class, R.xml.debug_settings).setSearchable(false); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6fdba4045d6..ea4268f6eec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -846,4 +846,6 @@ Show more Show less + + Resolution and format diff --git a/app/src/main/res/xml/main_settings_v2.xml b/app/src/main/res/xml/main_settings_v2.xml index 3fd7a5a3ae1..cb020306d7f 100644 --- a/app/src/main/res/xml/main_settings_v2.xml +++ b/app/src/main/res/xml/main_settings_v2.xml @@ -4,6 +4,11 @@ android:key="general_preferences" android:title="@string/settings"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +