From 859555e129367710603d0c392b38602336e9038c Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 26 Jul 2025 15:43:36 +0200 Subject: [PATCH 1/5] Use regions --- .../newpipe/player/helper/PlayerHelper.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index a110a80d6..791346d66 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -89,9 +89,7 @@ public final class PlayerHelper { private PlayerHelper() { } - //////////////////////////////////////////////////////////////////////////// - // Exposed helpers - //////////////////////////////////////////////////////////////////////////// + // region Exposed helpers @NonNull public static String getTimeString(final int milliSeconds) { @@ -219,9 +217,8 @@ public final class PlayerHelper { ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); } - //////////////////////////////////////////////////////////////////////////// - // Settings Resolution - //////////////////////////////////////////////////////////////////////////// + // endregion + // region Resolution public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) { return getPreferences(context) @@ -405,9 +402,8 @@ public final class PlayerHelper { return Integer.parseInt(preferredIntervalBytes) * 1024; } - //////////////////////////////////////////////////////////////////////////// - // Private helpers - //////////////////////////////////////////////////////////////////////////// + // endregion + // region Private helpers @NonNull private static SharedPreferences getPreferences(@NonNull final Context context) { @@ -427,9 +423,8 @@ public final class PlayerHelper { } - //////////////////////////////////////////////////////////////////////////// - // Utils used by player - //////////////////////////////////////////////////////////////////////////// + // endregion + // region Utils used by player @RepeatMode public static int nextRepeatMode(@RepeatMode final int repeatMode) { @@ -503,4 +498,6 @@ public final class PlayerHelper { player.getContext().getString(R.string.seek_duration_key), player.getContext().getString(R.string.seek_duration_default_value)))); } + + // endregion } From 70841db92ff1d8208acdd687973c40c9a76b01d6 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 26 Jul 2025 15:43:46 +0200 Subject: [PATCH 2/5] Cleanup ``Localization`` formatting --- .../main/java/org/schabi/newpipe/util/Localization.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 2bd4664ae..bd5463088 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -127,14 +127,13 @@ public final class Localization { } public static String localizeNumber(final double number) { - final NumberFormat nf = NumberFormat.getInstance(getAppLocale()); - return nf.format(number); + return NumberFormat.getInstance(getAppLocale()).format(number); } public static String formatDate(@NonNull final OffsetDateTime offsetDateTime) { return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) - .withLocale(getAppLocale()).format(offsetDateTime - .atZoneSameInstant(ZoneId.systemDefault())); + .withLocale(getAppLocale()) + .format(offsetDateTime.atZoneSameInstant(ZoneId.systemDefault())); } @SuppressLint("StringFormatInvalid") From ebd5e1a3180671ec0c5b2e3b72a67dbf28d8e543 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 26 Jul 2025 16:01:25 +0200 Subject: [PATCH 3/5] Remove unused method --- .../schabi/newpipe/player/helper/PlayerHelper.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 791346d66..d3e10da5d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -117,18 +117,6 @@ public final class PlayerHelper { return PITCH_FORMATTER.format(pitch); } - @NonNull - public static String subtitleMimeTypesOf(@NonNull final MediaFormat format) { - switch (format) { - case VTT: - return MimeTypes.TEXT_VTT; - case TTML: - return MimeTypes.APPLICATION_TTML; - default: - throw new IllegalArgumentException("Unrecognized mime type: " + format.name()); - } - } - @NonNull public static String captionLanguageOf(@NonNull final Context context, @NonNull final SubtitlesStream subtitles) { From 893a1cb699f2e74af85ab70a57da82111b7b64b8 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 26 Jul 2025 16:06:44 +0200 Subject: [PATCH 4/5] Encapsulate Formatters in PlayerHelper and reset them when the language is changed/changing. This way they will be re-initialized on the next call. Also Remove a bunch of outdated/non-thread safe code (STRING_FORMATTER) --- .../newpipe/player/helper/PlayerHelper.java | 71 ++++++++++++++----- .../settings/ContentSettingsFragment.java | 3 + 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index d3e10da5d..266d65f36 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -33,11 +33,9 @@ import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; import com.google.android.exoplayer2.ui.CaptionStyleCompat; -import com.google.android.exoplayer2.util.MimeTypes; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.SubtitlesStream; @@ -47,13 +45,14 @@ import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.util.ListHelper; +import org.schabi.newpipe.util.Localization; import java.lang.annotation.Retention; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; -import java.util.Formatter; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -62,11 +61,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; public final class PlayerHelper { - private static final StringBuilder STRING_BUILDER = new StringBuilder(); - private static final Formatter STRING_FORMATTER = - new Formatter(STRING_BUILDER, Locale.getDefault()); - private static final NumberFormat SPEED_FORMATTER = new DecimalFormat("0.##x"); - private static final NumberFormat PITCH_FORMATTER = new DecimalFormat("##%"); + private static final FormattersProvider FORMATTERS_PROVIDER = new FormattersProvider(); @Retention(SOURCE) @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI, @@ -91,6 +86,10 @@ public final class PlayerHelper { // region Exposed helpers + public static void resetFormat() { + FORMATTERS_PROVIDER.reset(); + } + @NonNull public static String getTimeString(final int milliSeconds) { final int seconds = (milliSeconds % 60000) / 1000; @@ -98,23 +97,24 @@ public final class PlayerHelper { final int hours = (milliSeconds % 86400000) / 3600000; final int days = (milliSeconds % (86400000 * 7)) / 86400000; - STRING_BUILDER.setLength(0); - return (days > 0 - ? STRING_FORMATTER.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds) - : hours > 0 - ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds) - : STRING_FORMATTER.format("%02d:%02d", minutes, seconds) - ).toString(); + final Formatters formatters = FORMATTERS_PROVIDER.formatters(); + if (days > 0) { + return formatters.stringFormat("%d:%02d:%02d:%02d", days, hours, minutes, seconds); + } + + return hours > 0 + ? formatters.stringFormat("%d:%02d:%02d", hours, minutes, seconds) + : formatters.stringFormat("%02d:%02d", minutes, seconds); } @NonNull public static String formatSpeed(final double speed) { - return SPEED_FORMATTER.format(speed); + return FORMATTERS_PROVIDER.formatters().speed().format(speed); } @NonNull public static String formatPitch(final double pitch) { - return PITCH_FORMATTER.format(pitch); + return FORMATTERS_PROVIDER.formatters().pitch().format(pitch); } @NonNull @@ -487,5 +487,42 @@ public final class PlayerHelper { player.getContext().getString(R.string.seek_duration_default_value)))); } + // endregion + // region Format + + static class FormattersProvider { + private Formatters formatters; + + public Formatters formatters() { + if (formatters == null) { + formatters = Formatters.create(); + } + return formatters; + } + + public void reset() { + formatters = null; + } + } + + record Formatters( + Locale locale, + NumberFormat speed, + NumberFormat pitch) { + + static Formatters create() { + final Locale locale = Localization.getAppLocale(); + final DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale); + return new Formatters( + locale, + new DecimalFormat("0.##x", dfs), + new DecimalFormat("##%", dfs)); + } + + String stringFormat(final String format, final Object... args) { + return String.format(locale, format, args); + } + } + // endregion } 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 ffc80e30e..e1d0e7be3 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -16,6 +16,7 @@ import androidx.preference.Preference; import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.image.ImageStrategy; import org.schabi.newpipe.util.image.PicassoHelper; @@ -53,6 +54,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { final Intent intent = new Intent(Settings.ACTION_APP_LOCALE_SETTINGS) .setData(Uri.fromParts("package", requireContext().getPackageName(), null)); startActivity(intent); + PlayerHelper.resetFormat(); return true; }); newAppLanguagePref.setVisible(true); @@ -64,6 +66,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { final String systemLang = getString(R.string.default_localization_key); final String tag = systemLang.equals(language) ? null : language; AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(tag)); + PlayerHelper.resetFormat(); return true; }); } From eea2b7417efa09d04b6c88aae9d76266d6852297 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 28 Jul 2025 15:29:06 +0200 Subject: [PATCH 5/5] Fix player formatters resetting too early on language change formatters() is called again by the player before the user has a chance to click on the language in the language chooser. So the correct solution would probably be to attach to https://developer.android.com/reference/android/content/Intent#ACTION_LOCALE_CHANGED, but let's keep it simple. I added `PlayerHelper.resetFormat();` in `ContentSettingsFragment.onDestroy()` and it works. It will mean the player formatters will be reset every time the user exits content settings, but whatever. --- .../org/schabi/newpipe/settings/ContentSettingsFragment.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 e1d0e7be3..b855f7c38 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -54,7 +54,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { final Intent intent = new Intent(Settings.ACTION_APP_LOCALE_SETTINGS) .setData(Uri.fromParts("package", requireContext().getPackageName(), null)); startActivity(intent); - PlayerHelper.resetFormat(); return true; }); newAppLanguagePref.setVisible(true); @@ -66,7 +65,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { final String systemLang = getString(R.string.default_localization_key); final String tag = systemLang.equals(language) ? null : language; AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(tag)); - PlayerHelper.resetFormat(); return true; }); } @@ -110,5 +108,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { NewPipe.setupLocalization( Localization.getPreferredLocalization(context), Localization.getPreferredContentCountry(context)); + PlayerHelper.resetFormat(); } }