1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-07-02 01:52:51 +00:00

Merge branch 'dev' into Merge-dev-to-refactor

# Conflicts:
#	app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
This commit is contained in:
Isira Seneviratne 2025-04-14 07:27:38 +05:30
commit 7615f79aca
11 changed files with 127 additions and 17 deletions

View File

@ -101,6 +101,10 @@ android {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
} }
androidResources {
generateLocaleConfig = true
}
buildFeatures { buildFeatures {
viewBinding true viewBinding true
compose true compose true

View File

@ -186,6 +186,8 @@ public class MainActivity extends AppCompatActivity {
&& ReleaseVersionUtil.INSTANCE.isReleaseApk()) { && ReleaseVersionUtil.INSTANCE.isReleaseApk()) {
UpdateSettingsFragment.askForConsentToUpdateChecks(this); UpdateSettingsFragment.askForConsentToUpdateChecks(this);
} }
Localization.migrateAppLanguageSettingIfNecessary(getApplicationContext());
} }
@Override @Override

View File

@ -1,10 +1,15 @@
package org.schabi.newpipe.settings; package org.schabi.newpipe.settings;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.Preference; import androidx.preference.Preference;
import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.DownloaderImpl;
@ -15,13 +20,13 @@ import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.util.image.ImageStrategy; import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PreferredImageQuality; import org.schabi.newpipe.util.image.PreferredImageQuality;
import java.util.Locale;
import coil3.SingletonImageLoader; import coil3.SingletonImageLoader;
public class ContentSettingsFragment extends BasePreferenceFragment { public class ContentSettingsFragment extends BasePreferenceFragment {
private String youtubeRestrictedModeEnabledKey; private String youtubeRestrictedModeEnabledKey;
private Localization initialSelectedLocalization;
private ContentCountry initialSelectedContentCountry;
private String initialLanguage; private String initialLanguage;
@Override @Override
@ -30,12 +35,28 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
addPreferencesFromResourceRegistry(); addPreferencesFromResourceRegistry();
initialSelectedLocalization = org.schabi.newpipe.util.Localization
.getPreferredLocalization(requireContext());
initialSelectedContentCountry = org.schabi.newpipe.util.Localization
.getPreferredContentCountry(requireContext());
initialLanguage = defaultPreferences.getString(getString(R.string.app_language_key), "en"); initialLanguage = defaultPreferences.getString(getString(R.string.app_language_key), "en");
if (Build.VERSION.SDK_INT >= 33) {
requirePreference(R.string.app_language_key).setVisible(false);
final Preference newAppLanguagePref =
requirePreference(R.string.app_language_android_13_and_up_key);
newAppLanguagePref.setSummaryProvider(preference -> {
final Locale customLocale = AppCompatDelegate.getApplicationLocales().get(0);
if (customLocale != null) {
return customLocale.getDisplayName();
}
return getString(R.string.systems_language);
});
newAppLanguagePref.setOnPreferenceClickListener(preference -> {
final Intent intent = new Intent(Settings.ACTION_APP_LOCALE_SETTINGS)
.setData(Uri.fromParts("package", requireContext().getPackageName(), null));
startActivity(intent);
return true;
});
newAppLanguagePref.setVisible(true);
}
final Preference imageQualityPreference = requirePreference(R.string.image_quality_key); final Preference imageQualityPreference = requirePreference(R.string.image_quality_key);
imageQualityPreference.setOnPreferenceChangeListener( imageQualityPreference.setOnPreferenceChangeListener(
(preference, newValue) -> { (preference, newValue) -> {
@ -70,19 +91,21 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
final Localization selectedLocalization = org.schabi.newpipe.util.Localization
.getPreferredLocalization(requireContext());
final ContentCountry selectedContentCountry = org.schabi.newpipe.util.Localization
.getPreferredContentCountry(requireContext());
final String selectedLanguage = final String selectedLanguage =
defaultPreferences.getString(getString(R.string.app_language_key), "en"); defaultPreferences.getString(getString(R.string.app_language_key), "en");
if (!selectedLocalization.equals(initialSelectedLocalization) if (!selectedLanguage.equals(initialLanguage)) {
|| !selectedContentCountry.equals(initialSelectedContentCountry) if (Build.VERSION.SDK_INT < 33) {
|| !selectedLanguage.equals(initialLanguage)) { Toast.makeText(
Toast.makeText(requireContext(), R.string.localization_changes_requires_app_restart, requireContext(),
Toast.LENGTH_LONG).show(); R.string.localization_changes_requires_app_restart,
Toast.LENGTH_LONG
).show();
}
final Localization selectedLocalization = org.schabi.newpipe.util.Localization
.getPreferredLocalization(requireContext());
final ContentCountry selectedContentCountry = org.schabi.newpipe.util.Localization
.getPreferredContentCountry(requireContext());
NewPipe.setupLocalization(selectedLocalization, selectedContentCountry); NewPipe.setupLocalization(selectedLocalization, selectedContentCountry);
} }
} }

View File

@ -12,12 +12,15 @@ import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes; import androidx.annotation.PluralsRes;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.math.MathUtils; import androidx.core.math.MathUtils;
import androidx.core.os.LocaleListCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.ocpsoft.prettytime.PrettyTime; import org.ocpsoft.prettytime.PrettyTime;
@ -39,6 +42,7 @@ import java.time.format.FormatStyle;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -63,6 +67,7 @@ import java.util.stream.Collectors;
*/ */
public final class Localization { public final class Localization {
private static final String TAG = Localization.class.toString();
public static final String DOT_SEPARATOR = ""; public static final String DOT_SEPARATOR = "";
private static PrettyTime prettyTime; private static PrettyTime prettyTime;
@ -101,6 +106,10 @@ public final class Localization {
} }
public static Locale getAppLocale(@NonNull final Context context) { public static Locale getAppLocale(@NonNull final Context context) {
if (Build.VERSION.SDK_INT >= 33) {
final Locale customLocale = AppCompatDelegate.getApplicationLocales().get(0);
return Objects.requireNonNullElseGet(customLocale, Locale::getDefault);
}
return getLocaleFromPrefs(context, R.string.app_language_key); return getLocaleFromPrefs(context, R.string.app_language_key);
} }
@ -422,4 +431,32 @@ public final class Localization {
final int safeCount = (int) MathUtils.clamp(count, Integer.MIN_VALUE, Integer.MAX_VALUE); final int safeCount = (int) MathUtils.clamp(count, Integer.MIN_VALUE, Integer.MAX_VALUE);
return context.getResources().getQuantityString(pluralId, safeCount, formattedCount); return context.getResources().getQuantityString(pluralId, safeCount, formattedCount);
} }
public static void migrateAppLanguageSettingIfNecessary(@NonNull final Context context) {
// Starting with pull request #12093, NewPipe on Android 13+ exclusively uses Android's
// public per-app language APIs to read and set the UI language for NewPipe.
// If running on Android 13+, the following code will migrate any existing custom
// app language in SharedPreferences to use the public per-app language APIs instead.
if (Build.VERSION.SDK_INT >= 33) {
final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
final String appLanguageKey = context.getString(R.string.app_language_key);
final String appLanguageValue = sp.getString(appLanguageKey, null);
if (appLanguageValue != null) {
sp.edit().remove(appLanguageKey).apply();
final String appLanguageDefaultValue =
context.getString(R.string.default_localization_key);
if (!appLanguageValue.equals(appLanguageDefaultValue)) {
try {
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.forLanguageTags(appLanguageValue)
);
} catch (final RuntimeException e) {
Log.e(TAG, "Failed to migrate previous custom app language "
+ "setting to public per-app language APIs"
);
}
}
}
}
}
} }

View File

@ -17,7 +17,7 @@ fun parseChallengeData(rawChallengeData: String): String {
val descrambled = descramble(scrambled.getString(1)) val descrambled = descramble(scrambled.getString(1))
JsonParser.array().from(descrambled) JsonParser.array().from(descrambled)
} else { } else {
scrambled.getArray(1) scrambled.getArray(0)
} }
val messageId = challengeData.getString(0) val messageId = challengeData.getString(0)

View File

@ -71,6 +71,9 @@ import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.Date;
import java.util.Locale;
import java.text.DateFormat;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observable;
@ -208,11 +211,17 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
h.pause.setTitle(mission.unknownLength ? R.string.stop : R.string.pause); h.pause.setTitle(mission.unknownLength ? R.string.stop : R.string.pause);
updateProgress(h); updateProgress(h);
mPendingDownloadsItems.add(h); mPendingDownloadsItems.add(h);
h.date.setText("");
} else { } else {
h.progress.setMarquee(false); h.progress.setMarquee(false);
h.status.setText("100%"); h.status.setText("100%");
h.progress.setProgress(1.0f); h.progress.setProgress(1.0f);
h.size.setText(Utility.formatBytes(item.mission.length)); h.size.setText(Utility.formatBytes(item.mission.length));
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault());
Date date = new Date(item.mission.timestamp);
h.date.setText(dateFormat.format(date));
} }
} }
@ -832,6 +841,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
ImageView icon; ImageView icon;
TextView name; TextView name;
TextView size; TextView size;
TextView date;
ProgressDrawable progress; ProgressDrawable progress;
PopupMenu popupMenu; PopupMenu popupMenu;
@ -862,6 +872,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
name = itemView.findViewById(R.id.item_name); name = itemView.findViewById(R.id.item_name);
icon = itemView.findViewById(R.id.item_icon); icon = itemView.findViewById(R.id.item_icon);
size = itemView.findViewById(R.id.item_size); size = itemView.findViewById(R.id.item_size);
date = itemView.findViewById(R.id.item_date);
name.setSelected(true); name.setSelected(true);

View File

@ -82,6 +82,18 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="12sp" /> android:textSize="12sp" />
<org.schabi.newpipe.views.NewPipeTextView
android:id="@+id/item_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/item_name"
android:layout_alignParentRight="true"
android:padding="6dp"
android:singleLine="true"
android:text=""
android:textColor="@color/white"
android:textSize="12sp" />
</RelativeLayout> </RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -62,6 +62,18 @@
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" /> android:textStyle="bold" />
<org.schabi.newpipe.views.NewPipeTextView
android:id="@+id/item_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/item_name"
android:layout_toLeftOf="@id/item_more"
android:padding="6dp"
android:singleLine="true"
android:text=""
android:textColor="@color/white"
android:textSize="12sp" />
<ImageView <ImageView
android:id="@+id/item_more" android:id="@+id/item_more"
style="?attr/buttonBarButtonStyle" style="?attr/buttonBarButtonStyle"

View File

@ -0,0 +1 @@
unqualifiedResLocale=en-US

View File

@ -353,6 +353,7 @@
<string name="playback_skip_silence_key">playback_skip_silence_key</string> <string name="playback_skip_silence_key">playback_skip_silence_key</string>
<string name="app_language_key">app_language_key</string> <string name="app_language_key">app_language_key</string>
<string name="app_language_android_13_and_up_key">app_language_android_13_and_up_key</string>
<string name="feed_update_threshold_key">feed_update_threshold_key</string> <string name="feed_update_threshold_key">feed_update_threshold_key</string>
<string name="feed_update_threshold_default_value">300</string> <string name="feed_update_threshold_default_value">300</string>

View File

@ -13,6 +13,13 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<Preference
android:key="@string/app_language_android_13_and_up_key"
android:title="@string/app_language_title"
app:isPreferenceVisible="false"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
<ListPreference <ListPreference
android:defaultValue="@string/default_localization_key" android:defaultValue="@string/default_localization_key"
android:entries="@array/language_names" android:entries="@array/language_names"