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
This commit is contained in:
Siddhesh Naik 2024-02-27 03:40:53 +05:30
parent 89645fab99
commit 5ca85861e7
5 changed files with 369 additions and 1 deletions

View File

@ -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.
* <br></br>
* 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<ListPreference>(
getString(R.string.default_resolution_key)
)
val defaultPopupResolution = findPreference<ListPreference>(
getString(R.string.default_popup_resolution_key)
)
val mobileDataResolution = findPreference<ListPreference>(
getString(R.string.limit_mobile_data_usage_key)
)
// update resolution preferences with new resolutions, entries & values for each
defaultResolution?.entries = resolutionListDescriptions.toTypedArray<String>()
defaultResolution?.entryValues = resolutionListValues.toTypedArray<String>()
defaultPopupResolution?.entries = resolutionListDescriptions.toTypedArray<String>()
defaultPopupResolution?.entryValues = resolutionListValues.toTypedArray<String>()
mobileDataResolution?.entries = limitDataUsageResolutionDescriptions.toTypedArray<String>()
mobileDataResolution?.entryValues =
limitDataUsageResolutionValues.toTypedArray<String>()
// 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<String>()
val displayedDescriptionValues = LinkedList<String>()
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<ListPreference>(getString(R.string.seek_duration_key))?.apply {
entryValues = displayedDurationValues.toTypedArray<CharSequence>()
entries = displayedDescriptionValues.toTypedArray<CharSequence>()
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)
}
}

View File

@ -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);

View File

@ -846,4 +846,6 @@
</plurals>
<string name="show_more">Show more</string>
<string name="show_less">Show less</string>
<!-- Settings redesign -->
<string name="settings_category_resolution_and_format_title">Resolution and format</string>
</resources>

View File

@ -4,6 +4,11 @@
android:key="general_preferences"
android:title="@string/settings">
<PreferenceScreen
android:fragment="org.schabi.newpipe.settings.PlayerSettingsFragment"
android:title="@string/settings_category_player_title"
app:iconSpaceReserved="false" />
<PreferenceScreen
android:fragment="org.schabi.newpipe.settings.DebugSettingsFragment"
android:key="@string/debug_pref_screen_key"

View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/settings_category_player_title">
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_resolution_and_format_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false">
<ListPreference
android:defaultValue="@string/default_resolution_value"
android:entries="@array/resolution_list_description"
android:entryValues="@array/resolution_list_values"
android:key="@string/default_resolution_key"
android:title="@string/default_resolution_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false"
app:useSimpleSummaryProvider="true" />
<ListPreference
android:defaultValue="@string/default_popup_resolution_value"
android:entries="@array/resolution_list_description"
android:entryValues="@array/resolution_list_values"
android:key="@string/default_popup_resolution_key"
android:title="@string/default_popup_resolution_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false"
app:useSimpleSummaryProvider="true" />
<ListPreference
android:defaultValue="@string/limit_mobile_data_usage_value"
android:entries="@array/limit_data_usage_description_list"
android:entryValues="@array/limit_data_usage_values_list"
android:key="@string/limit_mobile_data_usage_key"
android:title="@string/limit_mobile_data_usage_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/show_higher_resolutions_key"
android:summary="@string/show_higher_resolutions_summary"
android:title="@string/show_higher_resolutions_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<ListPreference
android:defaultValue="@string/default_video_format_value"
android:entries="@array/video_format_description_list"
android:entryValues="@array/video_format_values_list"
android:key="@string/default_video_format_key"
android:title="@string/default_video_format_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false"
app:useSimpleSummaryProvider="true" />
<ListPreference
android:defaultValue="@string/default_audio_format_value"
android:entries="@array/audio_format_description_list"
android:entryValues="@array/audio_format_values_list"
android:key="@string/default_audio_format_key"
android:title="@string/default_audio_format_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/prefer_original_audio_key"
android:summary="@string/prefer_original_audio_summary"
android:title="@string/prefer_original_audio_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/prefer_descriptive_audio_key"
android:summary="@string/prefer_descriptive_audio_summary"
android:title="@string/prefer_descriptive_audio_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<PreferenceScreen
android:fragment="org.schabi.newpipe.settings.ExoPlayerSettingsFragment"
android:key="@string/exoplayer_settings_key"
android:summary="@string/settings_category_exoplayer_summary"
android:title="@string/settings_category_exoplayer_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
</PreferenceCategory>
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_player_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false">
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/use_external_video_player_key"
android:summary="@string/use_external_video_player_summary"
android:title="@string/use_external_video_player_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/use_external_audio_player_key"
android:title="@string/use_external_audio_player_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/show_play_with_kodi_key"
android:summary="@string/show_play_with_kodi_summary"
android:title="@string/show_play_with_kodi_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<ListPreference
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:defaultValue="seekbar_preview_thumbnail_high_quality"
android:entries="@array/seekbar_preview_thumbnail_type_description"
android:entryValues="@array/seekbar_preview_thumbnail_type_key"
android:key="@string/seekbar_preview_thumbnail_key"
android:title="@string/seekbar_preview_thumbnail_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false"
app:useSimpleSummaryProvider="true" />
<Preference
android:key="@string/caption_settings_key"
android:summary="@string/caption_setting_description"
android:title="@string/caption_setting_title"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
<PreferenceScreen
android:key="@string/player_notification_screen_key"
android:summary="@string/settings_category_player_notification_summary"
android:title="@string/settings_category_player_notification_title"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
</PreferenceCategory>
</PreferenceScreen>