From 273b09a3e87d663ac958d2e9ae418728497a9571 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Sat, 21 Feb 2026 00:06:12 +0800 Subject: [PATCH 1/3] App: Migrate from Java to Kotlin Based on changes from refactor branch for code parity Signed-off-by: Aayush Gupta --- app/src/main/java/org/schabi/newpipe/App.java | 285 ------------------ app/src/main/java/org/schabi/newpipe/App.kt | 285 ++++++++++++++++++ .../java/org/schabi/newpipe/MainActivity.java | 4 +- .../info_list/dialog/InfoItemDialog.java | 2 +- .../newpipe/local/feed/FeedViewModel.kt | 2 +- .../newpipe/player/helper/PlayerHolder.java | 2 +- .../newpipe/settings/NewPipeSettings.java | 2 +- .../settings/migration/SettingMigrations.java | 2 +- .../org/schabi/newpipe/util/DeviceUtils.java | 4 +- .../schabi/newpipe/util/PermissionHelper.java | 4 +- .../schabi/newpipe/util/ReleaseVersionUtil.kt | 2 +- .../util/potoken/PoTokenProviderImpl.kt | 2 +- 12 files changed, 298 insertions(+), 298 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/App.java create mode 100644 app/src/main/java/org/schabi/newpipe/App.kt diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java deleted file mode 100644 index cf41aad46..000000000 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ /dev/null @@ -1,285 +0,0 @@ -package org.schabi.newpipe; - -import android.app.Application; -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.core.app.NotificationChannelCompat; -import androidx.core.app.NotificationManagerCompat; -import androidx.preference.PreferenceManager; - -import com.jakewharton.processphoenix.ProcessPhoenix; - -import org.acra.ACRA; -import org.acra.config.CoreConfigurationBuilder; -import org.schabi.newpipe.error.ReCaptchaActivity; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.downloader.Downloader; -import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor; -import org.schabi.newpipe.ktx.ExceptionUtils; -import org.schabi.newpipe.settings.NewPipeSettings; -import org.schabi.newpipe.util.BridgeStateSaverInitializer; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.ServiceHelper; -import org.schabi.newpipe.util.StateSaver; -import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; -import org.schabi.newpipe.util.image.PreferredImageQuality; -import org.schabi.newpipe.util.potoken.PoTokenProviderImpl; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.SocketException; -import java.util.List; -import java.util.Objects; - -import io.reactivex.rxjava3.exceptions.CompositeException; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException; -import io.reactivex.rxjava3.exceptions.UndeliverableException; -import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; - -/* - * Copyright (C) Hans-Christoph Steiner 2016 - * App.java is part of NewPipe. - * - * NewPipe is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * NewPipe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with NewPipe. If not, see . - */ - -public class App extends Application { - public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; - private static final String TAG = App.class.toString(); - - private boolean isFirstRun = false; - private boolean notificationsRequested = false; - - private static App app; - - @NonNull - public static App getApp() { - return app; - } - - public boolean getNotificationsRequested() { - return notificationsRequested; - } - - public void setNotificationsRequested() { - notificationsRequested = true; - } - - @Override - protected void attachBaseContext(final Context base) { - super.attachBaseContext(base); - initACRA(); - } - - @Override - public void onCreate() { - super.onCreate(); - - app = this; - - if (ProcessPhoenix.isPhoenixProcess(this)) { - Log.i(TAG, "This is a phoenix process! " - + "Aborting initialization of App[onCreate]"); - return; - } - - // check if the last used preference version is set - // to determine whether this is the first app run - final int lastUsedPrefVersion = PreferenceManager.getDefaultSharedPreferences(this) - .getInt(getString(R.string.last_used_preferences_version), -1); - isFirstRun = lastUsedPrefVersion == -1; - - // Initialize settings first because other initializations can use its values - NewPipeSettings.initSettings(this); - - NewPipe.init(getDownloader(), - Localization.getPreferredLocalization(this), - Localization.getPreferredContentCountry(this)); - Localization.initPrettyTime(Localization.resolvePrettyTime()); - - BridgeStateSaverInitializer.init(this); - StateSaver.init(this); - initNotificationChannels(); - - ServiceHelper.initServices(this); - - // Initialize image loader - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - PicassoHelper.init(this); - ImageStrategy.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(this, - prefs.getString(getString(R.string.image_quality_key), - getString(R.string.image_quality_default)))); - PicassoHelper.setIndicatorsEnabled(MainActivity.DEBUG - && prefs.getBoolean(getString(R.string.show_image_indicators_key), false)); - - configureRxJavaErrorHandler(); - - YoutubeStreamExtractor.setPoTokenProvider(PoTokenProviderImpl.INSTANCE); - } - - @Override - public void onTerminate() { - super.onTerminate(); - PicassoHelper.terminate(); - } - - protected Downloader getDownloader() { - final DownloaderImpl downloader = DownloaderImpl.init(null); - setCookiesToDownloader(downloader); - return downloader; - } - - protected void setCookiesToDownloader(final DownloaderImpl downloader) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( - getApplicationContext()); - final String key = getApplicationContext().getString(R.string.recaptcha_cookies_key); - downloader.setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, prefs.getString(key, null)); - downloader.updateYoutubeRestrictedModeCookies(getApplicationContext()); - } - - private void configureRxJavaErrorHandler() { - // https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling - RxJavaPlugins.setErrorHandler(new Consumer() { - @Override - public void accept(@NonNull final Throwable throwable) { - Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : " - + "throwable = [" + throwable.getClass().getName() + "]"); - - final Throwable actualThrowable; - if (throwable instanceof UndeliverableException) { - // As UndeliverableException is a wrapper, - // get the cause of it to get the "real" exception - actualThrowable = Objects.requireNonNull(throwable.getCause()); - } else { - actualThrowable = throwable; - } - - final List errors; - if (actualThrowable instanceof CompositeException) { - errors = ((CompositeException) actualThrowable).getExceptions(); - } else { - errors = List.of(actualThrowable); - } - - for (final Throwable error : errors) { - if (isThrowableIgnored(error)) { - return; - } - if (isThrowableCritical(error)) { - reportException(error); - return; - } - } - - // Out-of-lifecycle exceptions should only be reported if a debug user wishes so, - // When exception is not reported, log it - if (isDisposedRxExceptionsReported()) { - reportException(actualThrowable); - } else { - Log.e(TAG, "RxJavaPlugin: Undeliverable Exception received: ", actualThrowable); - } - } - - private boolean isThrowableIgnored(@NonNull final Throwable throwable) { - // Don't crash the application over a simple network problem - return ExceptionUtils.hasAssignableCause(throwable, - // network api cancellation - IOException.class, SocketException.class, - // blocking code disposed - InterruptedException.class, InterruptedIOException.class); - } - - private boolean isThrowableCritical(@NonNull final Throwable throwable) { - // Though these exceptions cannot be ignored - return ExceptionUtils.hasAssignableCause(throwable, - NullPointerException.class, IllegalArgumentException.class, // bug in app - OnErrorNotImplementedException.class, MissingBackpressureException.class, - IllegalStateException.class); // bug in operator - } - - private void reportException(@NonNull final Throwable throwable) { - // Throw uncaught exception that will trigger the report system - Thread.currentThread().getUncaughtExceptionHandler() - .uncaughtException(Thread.currentThread(), throwable); - } - }); - } - - /** - * Called in {@link #attachBaseContext(Context)} after calling the {@code super} method. - * Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA. - */ - protected void initACRA() { - if (ACRA.isACRASenderServiceProcess()) { - return; - } - - final CoreConfigurationBuilder acraConfig = new CoreConfigurationBuilder() - .withBuildConfigClass(BuildConfig.class); - ACRA.init(this, acraConfig); - } - - private void initNotificationChannels() { - // Keep the importance below DEFAULT to avoid making noise on every notification update for - // the main and update channels - final List notificationChannelCompats = List.of( - new NotificationChannelCompat.Builder(getString(R.string.notification_channel_id), - NotificationManagerCompat.IMPORTANCE_LOW) - .setName(getString(R.string.notification_channel_name)) - .setDescription(getString(R.string.notification_channel_description)) - .build(), - new NotificationChannelCompat - .Builder(getString(R.string.app_update_notification_channel_id), - NotificationManagerCompat.IMPORTANCE_LOW) - .setName(getString(R.string.app_update_notification_channel_name)) - .setDescription( - getString(R.string.app_update_notification_channel_description)) - .build(), - new NotificationChannelCompat.Builder(getString(R.string.hash_channel_id), - NotificationManagerCompat.IMPORTANCE_HIGH) - .setName(getString(R.string.hash_channel_name)) - .setDescription(getString(R.string.hash_channel_description)) - .build(), - new NotificationChannelCompat.Builder(getString(R.string.error_report_channel_id), - NotificationManagerCompat.IMPORTANCE_LOW) - .setName(getString(R.string.error_report_channel_name)) - .setDescription(getString(R.string.error_report_channel_description)) - .build(), - new NotificationChannelCompat - .Builder(getString(R.string.streams_notification_channel_id), - NotificationManagerCompat.IMPORTANCE_DEFAULT) - .setName(getString(R.string.streams_notification_channel_name)) - .setDescription( - getString(R.string.streams_notification_channel_description)) - .build() - ); - - final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); - notificationManager.createNotificationChannelsCompat(notificationChannelCompats); - } - - protected boolean isDisposedRxExceptionsReported() { - return false; - } - - public boolean isFirstRun() { - return isFirstRun; - } -} diff --git a/app/src/main/java/org/schabi/newpipe/App.kt b/app/src/main/java/org/schabi/newpipe/App.kt new file mode 100644 index 000000000..ad6b82435 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/App.kt @@ -0,0 +1,285 @@ +package org.schabi.newpipe + +import android.app.Application +import android.content.Context +import android.util.Log +import androidx.core.app.NotificationChannelCompat +import androidx.core.app.NotificationManagerCompat +import androidx.preference.PreferenceManager +import com.jakewharton.processphoenix.ProcessPhoenix +import io.reactivex.rxjava3.exceptions.CompositeException +import io.reactivex.rxjava3.exceptions.MissingBackpressureException +import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException +import io.reactivex.rxjava3.exceptions.UndeliverableException +import io.reactivex.rxjava3.functions.Consumer +import io.reactivex.rxjava3.plugins.RxJavaPlugins +import java.io.IOException +import java.io.InterruptedIOException +import java.net.SocketException +import org.acra.ACRA.init +import org.acra.ACRA.isACRASenderServiceProcess +import org.acra.config.CoreConfigurationBuilder +import org.schabi.newpipe.error.ReCaptchaActivity +import org.schabi.newpipe.extractor.NewPipe +import org.schabi.newpipe.extractor.downloader.Downloader +import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor +import org.schabi.newpipe.ktx.hasAssignableCause +import org.schabi.newpipe.settings.NewPipeSettings +import org.schabi.newpipe.util.BridgeStateSaverInitializer +import org.schabi.newpipe.util.Localization +import org.schabi.newpipe.util.ServiceHelper +import org.schabi.newpipe.util.StateSaver +import org.schabi.newpipe.util.image.ImageStrategy.setPreferredImageQuality +import org.schabi.newpipe.util.image.PicassoHelper +import org.schabi.newpipe.util.image.PreferredImageQuality.Companion.fromPreferenceKey +import org.schabi.newpipe.util.potoken.PoTokenProviderImpl + +/* + * Copyright (C) Hans-Christoph Steiner 2016 + * App.kt is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ +open class App : Application() { + var isFirstRun = false + private set + var notificationsRequested = false + private set + + fun setNotificationsRequested() { + notificationsRequested = true + } + + override fun attachBaseContext(base: Context?) { + super.attachBaseContext(base) + initACRA() + } + + override fun onCreate() { + super.onCreate() + + instance = this + + if (ProcessPhoenix.isPhoenixProcess(this)) { + Log.i(TAG, "This is a phoenix process! Aborting initialization of App[onCreate]") + return + } + + // check if the last used preference version is set + // to determine whether this is the first app run + val lastUsedPrefVersion = + PreferenceManager + .getDefaultSharedPreferences(this) + .getInt(getString(R.string.last_used_preferences_version), -1) + isFirstRun = lastUsedPrefVersion == -1 + + // Initialize settings first because other initializations can use its values + NewPipeSettings.initSettings(this) + + NewPipe.init( + getDownloader(), + Localization.getPreferredLocalization(this), + Localization.getPreferredContentCountry(this) + ) + Localization.initPrettyTime(Localization.resolvePrettyTime()) + + BridgeStateSaverInitializer.init(this) + StateSaver.init(this) + initNotificationChannels() + + ServiceHelper.initServices(this) + + // Initialize image loader + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + PicassoHelper.init(this) + setPreferredImageQuality( + fromPreferenceKey( + this, + prefs.getString( + getString(R.string.image_quality_key), + getString(R.string.image_quality_default) + ) + ) + ) + PicassoHelper.setIndicatorsEnabled( + MainActivity.DEBUG && + prefs.getBoolean(getString(R.string.show_image_indicators_key), false) + ) + + configureRxJavaErrorHandler() + + YoutubeStreamExtractor.setPoTokenProvider(PoTokenProviderImpl) + } + + override fun onTerminate() { + super.onTerminate() + PicassoHelper.terminate() + } + + protected open fun getDownloader(): Downloader { + val downloader = DownloaderImpl.init(null) + setCookiesToDownloader(downloader) + return downloader + } + + protected fun setCookiesToDownloader(downloader: DownloaderImpl) { + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + val key = getString(R.string.recaptcha_cookies_key) + downloader.setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, prefs.getString(key, null)) + downloader.updateYoutubeRestrictedModeCookies(this) + } + + private fun configureRxJavaErrorHandler() { + // https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling + RxJavaPlugins.setErrorHandler( + object : Consumer { + override fun accept(throwable: Throwable) { + Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : throwable = [${throwable.javaClass.getName()}]") + + // As UndeliverableException is a wrapper, + // get the cause of it to get the "real" exception + val actualThrowable = (throwable as? UndeliverableException)?.cause ?: throwable + + val errors = (actualThrowable as? CompositeException)?.exceptions ?: listOf(actualThrowable) + + for (error in errors) { + if (isThrowableIgnored(error)) { + return + } + if (isThrowableCritical(error)) { + reportException(error) + return + } + } + + // Out-of-lifecycle exceptions should only be reported if a debug user wishes so, + // When exception is not reported, log it + if (isDisposedRxExceptionsReported()) { + reportException(actualThrowable) + } else { + Log.e(TAG, "RxJavaPlugin: Undeliverable Exception received: ", actualThrowable) + } + } + + fun isThrowableIgnored(throwable: Throwable): Boolean { + // Don't crash the application over a simple network problem + return throwable // network api cancellation + .hasAssignableCause( + IOException::class.java, + SocketException::class.java, // blocking code disposed + InterruptedException::class.java, + InterruptedIOException::class.java + ) + } + + fun isThrowableCritical(throwable: Throwable): Boolean { + // Though these exceptions cannot be ignored + return throwable + .hasAssignableCause( + // bug in app + NullPointerException::class.java, + IllegalArgumentException::class.java, + OnErrorNotImplementedException::class.java, + MissingBackpressureException::class.java, + // bug in operator + IllegalStateException::class.java + ) + } + + fun reportException(throwable: Throwable) { + // Throw uncaught exception that will trigger the report system + Thread + .currentThread() + .uncaughtExceptionHandler + .uncaughtException(Thread.currentThread(), throwable) + } + } + ) + } + + /** + * Called in [.attachBaseContext] after calling the `super` method. + * Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA. + */ + protected fun initACRA() { + if (isACRASenderServiceProcess()) { + return + } + + val acraConfig = + CoreConfigurationBuilder() + .withBuildConfigClass(BuildConfig::class.java) + init(this, acraConfig) + } + + private fun initNotificationChannels() { + // Keep the importance below DEFAULT to avoid making noise on every notification update for + // the main and update channels + val mainChannel = + NotificationChannelCompat + .Builder( + getString(R.string.notification_channel_id), + NotificationManagerCompat.IMPORTANCE_LOW + ).setName(getString(R.string.notification_channel_name)) + .setDescription(getString(R.string.notification_channel_description)) + .build() + val appUpdateChannel = + NotificationChannelCompat + .Builder( + getString(R.string.app_update_notification_channel_id), + NotificationManagerCompat.IMPORTANCE_LOW + ).setName(getString(R.string.app_update_notification_channel_name)) + .setDescription(getString(R.string.app_update_notification_channel_description)) + .build() + val hashChannel = + NotificationChannelCompat + .Builder( + getString(R.string.hash_channel_id), + NotificationManagerCompat.IMPORTANCE_HIGH + ).setName(getString(R.string.hash_channel_name)) + .setDescription(getString(R.string.hash_channel_description)) + .build() + val errorReportChannel = + NotificationChannelCompat + .Builder( + getString(R.string.error_report_channel_id), + NotificationManagerCompat.IMPORTANCE_LOW + ).setName(getString(R.string.error_report_channel_name)) + .setDescription(getString(R.string.error_report_channel_description)) + .build() + val newStreamChannel = + NotificationChannelCompat + .Builder( + getString(R.string.streams_notification_channel_id), + NotificationManagerCompat.IMPORTANCE_DEFAULT + ).setName(getString(R.string.streams_notification_channel_name)) + .setDescription(getString(R.string.streams_notification_channel_description)) + .build() + + val channels = listOf(mainChannel, appUpdateChannel, hashChannel, errorReportChannel, newStreamChannel) + + NotificationManagerCompat.from(this).createNotificationChannelsCompat(channels) + } + + protected open fun isDisposedRxExceptionsReported(): Boolean = false + + companion object { + const val PACKAGE_NAME: String = BuildConfig.APPLICATION_ID + private val TAG = App::class.java.toString() + + @JvmStatic + lateinit var instance: App + private set + } +} diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 3c0018d99..e4f4b5b8a 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -191,7 +191,7 @@ public class MainActivity extends AppCompatActivity { NotificationWorker.initialize(this); } if (!UpdateSettingsFragment.wasUserAskedForConsent(this) - && !App.getApp().isFirstRun() + && !App.getInstance().isFirstRun() && ReleaseVersionUtil.INSTANCE.isReleaseApk()) { UpdateSettingsFragment.askForConsentToUpdateChecks(this); } @@ -203,7 +203,7 @@ public class MainActivity extends AppCompatActivity { protected void onPostCreate(final Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - final App app = App.getApp(); + final App app = App.getInstance(); if (sharedPreferences.getBoolean(app.getString(R.string.update_app_key), false) && sharedPreferences diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java index 0c69557bf..dcf01e190 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java @@ -346,7 +346,7 @@ public final class InfoItemDialog { public static void reportErrorDuringInitialization(final Throwable throwable, final InfoItem item) { - ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo( + ErrorUtil.showSnackbar(App.getInstance().getBaseContext(), new ErrorInfo( throwable, UserAction.OPEN_INFO_ITEM_DIALOG, "none", diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt index 58ec818f3..19adf6eaa 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt @@ -166,7 +166,7 @@ class FeedViewModel( fun getFactory(context: Context, groupId: Long) = viewModelFactory { initializer { FeedViewModel( - App.getApp(), + App.instance, groupId, // Read initial value from preferences getShowPlayedItemsFromPreferences(context.applicationContext), diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java index 9edfc804a..daae6d54e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java @@ -117,7 +117,7 @@ public final class PlayerHolder { // helper to handle context in common place as using the same // context to bind/unbind a service is crucial private Context getCommonContext() { - return App.getApp(); + return App.getInstance(); } public void startService(final boolean playAfterConnect, 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 7cb1564b3..3cc29afee 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -157,7 +157,7 @@ public final class NewPipeSettings { prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0 && !prefs.getBoolean(disabledTunnelingKey, false); - if (App.getApp().isFirstRun() + if (App.getInstance().isFirstRun() || (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) { setMediaTunneling(context); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java index 67944075d..92520ec7e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java @@ -251,7 +251,7 @@ public final class SettingMigrations { final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); // no migration to run, already up to date - if (App.getApp().isFirstRun()) { + if (App.getInstance().isFirstRun()) { sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); return; } else if (lastPrefVersion == VERSION) { 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 9eb9fab37..83152a36d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java @@ -131,7 +131,7 @@ public final class DeviceUtils { } isFireTV = - App.getApp().getPackageManager().hasSystemFeature(AMAZON_FEATURE_FIRE_TV); + App.getInstance().getPackageManager().hasSystemFeature(AMAZON_FEATURE_FIRE_TV); return isFireTV; } @@ -140,7 +140,7 @@ public final class DeviceUtils { return isTV; } - final PackageManager pm = App.getApp().getPackageManager(); + final PackageManager pm = App.getInstance().getPackageManager(); // from doc: https://developer.android.com/training/tv/start/hardware.html#runtime-check boolean isTv = ContextCompat.getSystemService(context, UiModeManager.class) diff --git a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java index 969d787d7..2defbdc5b 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java @@ -90,10 +90,10 @@ public final class PermissionHelper { && ContextCompat.checkSelfPermission(activity, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (!App.getApp().getNotificationsRequested()) { + if (!App.getInstance().getNotificationsRequested()) { ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.POST_NOTIFICATIONS}, requestCode); - App.getApp().setNotificationsRequested(); + App.getInstance().setNotificationsRequested(); return false; } } diff --git a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt index bc3849384..31d42d751 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt @@ -21,7 +21,7 @@ object ReleaseVersionUtil { val certificates = mapOf( RELEASE_CERT_PUBLIC_KEY_SHA256.hexToByteArray() to PackageManager.CERT_INPUT_SHA256 ) - val app = App.getApp() + val app = App.instance try { PackageInfoCompat.hasSignatures(app.packageManager, app.packageName, certificates, false) } catch (e: PackageManager.NameNotFoundException) { diff --git a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt index 12fadb68d..53ae04a3c 100644 --- a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt +++ b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt @@ -78,7 +78,7 @@ object PoTokenProviderImpl : PoTokenProvider { // create a new webPoTokenGenerator webPoTokenGenerator = PoTokenWebView - .newPoTokenGenerator(App.getApp()).blockingGet() + .newPoTokenGenerator(App.instance).blockingGet() // The streaming poToken needs to be generated exactly once before generating // any other (player) tokens. From 57364109f4f357e32e0669ae9b87978422322244 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Sat, 21 Feb 2026 16:38:35 +0800 Subject: [PATCH 2/3] Migrate from Picasso to Coil Based on changes from refactor Signed-off-by: Aayush Gupta --- app/build.gradle.kts | 3 +- app/src/main/java/org/schabi/newpipe/App.kt | 38 +-- .../org/schabi/newpipe/DownloaderImpl.java | 5 + .../org/schabi/newpipe/about/AboutActivity.kt | 8 +- .../fragments/detail/VideoDetailFragment.java | 29 +-- .../list/channel/ChannelFragment.java | 18 +- .../list/comments/CommentRepliesFragment.java | 4 +- .../list/playlist/PlaylistFragment.java | 11 +- .../newpipe/info_list/StreamSegmentItem.kt | 44 ++-- .../holder/ChannelMiniInfoItemHolder.java | 4 +- .../holder/CommentInfoItemHolder.java | 8 +- .../holder/PlaylistMiniInfoItemHolder.java | 4 +- .../holder/StreamMiniInfoItemHolder.java | 4 +- .../java/org/schabi/newpipe/ktx/Bitmap.kt | 13 + .../newpipe/local/feed/item/StreamItem.kt | 4 +- .../feed/notifications/NotificationHelper.kt | 68 ++---- .../local/holder/LocalPlaylistItemHolder.java | 7 +- .../holder/LocalPlaylistStreamItemHolder.java | 6 +- .../LocalStatisticStreamItemHolder.java | 6 +- .../holder/RemotePlaylistItemHolder.java | 7 +- .../local/subscription/item/ChannelItem.kt | 4 +- .../item/PickerSubscriptionItem.kt | 4 +- .../org/schabi/newpipe/player/Player.java | 93 +++----- .../playqueue/PlayQueueItemBuilder.java | 4 +- .../SeekbarPreviewThumbnailHolder.java | 7 +- .../settings/ContentSettingsFragment.java | 16 +- .../settings/DebugSettingsFragment.java | 8 - .../settings/SelectChannelFragment.java | 4 +- .../settings/SelectPlaylistFragment.java | 18 +- .../external_communication/ShareUtils.java | 72 +++--- .../schabi/newpipe/util/image/CoilHelper.kt | 185 +++++++++++++++ .../newpipe/util/image/PicassoHelper.java | 224 ------------------ app/src/main/res/values/settings_keys.xml | 1 - app/src/main/res/xml/debug_settings.xml | 7 - gradle/libs.versions.toml | 6 +- 35 files changed, 432 insertions(+), 512 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt create mode 100644 app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt delete mode 100644 app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e72fcfcfc..e18826c16 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -272,7 +272,8 @@ dependencies { implementation(libs.lisawray.groupie.viewbinding) // Image loading - implementation(libs.squareup.picasso) + implementation(libs.coil.compose) + implementation(libs.coil.network.okhttp) // Markdown library for Android implementation(libs.noties.markwon.core) diff --git a/app/src/main/java/org/schabi/newpipe/App.kt b/app/src/main/java/org/schabi/newpipe/App.kt index ad6b82435..3ca259528 100644 --- a/app/src/main/java/org/schabi/newpipe/App.kt +++ b/app/src/main/java/org/schabi/newpipe/App.kt @@ -1,11 +1,19 @@ package org.schabi.newpipe +import android.app.ActivityManager import android.app.Application import android.content.Context import android.util.Log import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.getSystemService import androidx.preference.PreferenceManager +import coil3.ImageLoader +import coil3.SingletonImageLoader +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.util.DebugLogger import com.jakewharton.processphoenix.ProcessPhoenix import io.reactivex.rxjava3.exceptions.CompositeException import io.reactivex.rxjava3.exceptions.MissingBackpressureException @@ -29,9 +37,8 @@ import org.schabi.newpipe.util.BridgeStateSaverInitializer import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.StateSaver -import org.schabi.newpipe.util.image.ImageStrategy.setPreferredImageQuality -import org.schabi.newpipe.util.image.PicassoHelper -import org.schabi.newpipe.util.image.PreferredImageQuality.Companion.fromPreferenceKey +import org.schabi.newpipe.util.image.ImageStrategy +import org.schabi.newpipe.util.image.PreferredImageQuality import org.schabi.newpipe.util.potoken.PoTokenProviderImpl /* @@ -51,7 +58,9 @@ import org.schabi.newpipe.util.potoken.PoTokenProviderImpl * You should have received a copy of the GNU General Public License * along with NewPipe. If not, see . */ -open class App : Application() { +open class App : + Application(), + SingletonImageLoader.Factory { var isFirstRun = false private set var notificationsRequested = false @@ -102,9 +111,8 @@ open class App : Application() { // Initialize image loader val prefs = PreferenceManager.getDefaultSharedPreferences(this) - PicassoHelper.init(this) - setPreferredImageQuality( - fromPreferenceKey( + ImageStrategy.setPreferredImageQuality( + PreferredImageQuality.fromPreferenceKey( this, prefs.getString( getString(R.string.image_quality_key), @@ -112,20 +120,20 @@ open class App : Application() { ) ) ) - PicassoHelper.setIndicatorsEnabled( - MainActivity.DEBUG && - prefs.getBoolean(getString(R.string.show_image_indicators_key), false) - ) configureRxJavaErrorHandler() YoutubeStreamExtractor.setPoTokenProvider(PoTokenProviderImpl) } - override fun onTerminate() { - super.onTerminate() - PicassoHelper.terminate() - } + override fun newImageLoader(context: Context): ImageLoader = ImageLoader + .Builder(this) + .logger(if (BuildConfig.DEBUG) DebugLogger() else null) + .allowRgb565(getSystemService()!!.isLowRamDevice) + .crossfade(true) + .components { + add(OkHttpNetworkFetcherFactory(callFactory = DownloaderImpl.getInstance().client)) + }.build() protected open fun getDownloader(): Downloader { val downloader = DownloaderImpl.init(null) diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index 041e91396..74a2cab51 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -48,6 +48,11 @@ public final class DownloaderImpl extends Downloader { this.mCookies = new HashMap<>(); } + @NonNull + public OkHttpClient getClient() { + return client; + } + /** * It's recommended to call exactly once in the entire lifetime of the application. * diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt index 94bde796b..ed5951f04 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt @@ -207,10 +207,10 @@ class AboutActivity : AppCompatActivity() { StandardLicenses.APACHE2 ), SoftwareComponent( - "Picasso", - "2013", - "Square, Inc.", - "https://square.github.io/picasso/", + "Coil", + "2023", + "Coil Contributors", + "https://coil-kt.github.io/coil/", StandardLicenses.APACHE2 ), SoftwareComponent( diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 31a496711..c97d12ba2 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -118,7 +118,7 @@ import org.schabi.newpipe.util.StreamTypeUtil; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; -import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.image.CoilHelper; import java.util.ArrayList; import java.util.Iterator; @@ -129,6 +129,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import coil3.util.CoilUtils; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; @@ -160,8 +161,6 @@ public final class VideoDetailFragment private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB"; private static final String EMPTY_TAB_TAG = "EMPTY TAB"; - private static final String PICASSO_VIDEO_DETAILS_TAG = "PICASSO_VIDEO_DETAILS_TAG"; - // tabs private boolean showComments; private boolean showRelatedItems; @@ -1499,7 +1498,10 @@ public final class VideoDetailFragment } } - PicassoHelper.cancelTag(PICASSO_VIDEO_DETAILS_TAG); + CoilUtils.dispose(binding.detailThumbnailImageView); + CoilUtils.dispose(binding.detailSubChannelThumbnailView); + CoilUtils.dispose(binding.overlayThumbnail); + CoilUtils.dispose(binding.detailUploaderThumbnailView); binding.detailThumbnailImageView.setImageBitmap(null); binding.detailSubChannelThumbnailView.setImageBitmap(null); } @@ -1590,8 +1592,8 @@ public final class VideoDetailFragment binding.detailSecondaryControlPanel.setVisibility(View.GONE); checkUpdateProgressInfo(info); - PicassoHelper.loadDetailsThumbnail(info.getThumbnails()).tag(PICASSO_VIDEO_DETAILS_TAG) - .into(binding.detailThumbnailImageView); + CoilHelper.INSTANCE.loadDetailsThumbnail(binding.detailThumbnailImageView, + info.getThumbnails()); showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView, binding.detailMetaInfoSeparator, disposables); @@ -1641,8 +1643,8 @@ public final class VideoDetailFragment binding.detailUploaderTextView.setVisibility(View.GONE); } - PicassoHelper.loadAvatar(info.getUploaderAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) - .into(binding.detailSubChannelThumbnailView); + CoilHelper.INSTANCE.loadAvatar(binding.detailSubChannelThumbnailView, + info.getUploaderAvatars()); binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE); binding.detailUploaderThumbnailView.setVisibility(View.GONE); } @@ -1673,11 +1675,11 @@ public final class VideoDetailFragment binding.detailUploaderTextView.setVisibility(View.GONE); } - PicassoHelper.loadAvatar(info.getSubChannelAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) - .into(binding.detailSubChannelThumbnailView); + CoilHelper.INSTANCE.loadAvatar(binding.detailSubChannelThumbnailView, + info.getSubChannelAvatars()); binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE); - PicassoHelper.loadAvatar(info.getUploaderAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) - .into(binding.detailUploaderThumbnailView); + CoilHelper.INSTANCE.loadAvatar(binding.detailUploaderThumbnailView, + info.getUploaderAvatars()); binding.detailUploaderThumbnailView.setVisibility(View.VISIBLE); } @@ -2435,8 +2437,7 @@ public final class VideoDetailFragment binding.overlayTitleTextView.setText(isEmpty(overlayTitle) ? "" : overlayTitle); binding.overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader); binding.overlayThumbnail.setImageDrawable(null); - PicassoHelper.loadDetailsThumbnail(thumbnails).tag(PICASSO_VIDEO_DETAILS_TAG) - .into(binding.overlayThumbnail); + CoilHelper.INSTANCE.loadDetailsThumbnail(binding.overlayThumbnail, thumbnails); } private void setOverlayPlayPauseImage(final boolean playerIsPlaying) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 878ceb139..97481f25b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -53,13 +53,14 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; import java.util.List; import java.util.Queue; import java.util.concurrent.TimeUnit; +import coil3.util.CoilUtils; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.CompositeDisposable; @@ -73,7 +74,6 @@ public class ChannelFragment extends BaseStateFragment implements StateSaver.WriteRead { private static final int BUTTON_DEBOUNCE_INTERVAL = 100; - private static final String PICASSO_CHANNEL_TAG = "PICASSO_CHANNEL_TAG"; @State protected int serviceId = Constants.NO_SERVICE_ID; @@ -578,7 +578,9 @@ public class ChannelFragment extends BaseStateFragment @Override public void showLoading() { super.showLoading(); - PicassoHelper.cancelTag(PICASSO_CHANNEL_TAG); + CoilUtils.dispose(binding.channelAvatarView); + CoilUtils.dispose(binding.channelBannerImage); + CoilUtils.dispose(binding.subChannelAvatarView); animate(binding.channelSubscribeButton, false, 100); } @@ -589,17 +591,15 @@ public class ChannelFragment extends BaseStateFragment setInitialData(result.getServiceId(), result.getOriginalUrl(), result.getName()); if (ImageStrategy.shouldLoadImages() && !result.getBanners().isEmpty()) { - PicassoHelper.loadBanner(result.getBanners()).tag(PICASSO_CHANNEL_TAG) - .into(binding.channelBannerImage); + CoilHelper.INSTANCE.loadBanner(binding.channelBannerImage, result.getBanners()); } else { // do not waste space for the banner, if the user disabled images or there is not one binding.channelBannerImage.setImageDrawable(null); } - PicassoHelper.loadAvatar(result.getAvatars()).tag(PICASSO_CHANNEL_TAG) - .into(binding.channelAvatarView); - PicassoHelper.loadAvatar(result.getParentChannelAvatars()).tag(PICASSO_CHANNEL_TAG) - .into(binding.subChannelAvatarView); + CoilHelper.INSTANCE.loadAvatar(binding.channelAvatarView, result.getAvatars()); + CoilHelper.INSTANCE.loadAvatar(binding.subChannelAvatarView, + result.getParentChannelAvatars()); binding.channelTitleView.setText(result.getName()); binding.channelSubscriberView.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java index ce52c029d..ed7dd5a8c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java @@ -25,8 +25,8 @@ import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.text.TextLinkifier; import org.schabi.newpipe.util.text.LongPressLinkMovementMethod; @@ -84,7 +84,7 @@ public final class CommentRepliesFragment final CommentsInfoItem item = commentsInfoItem; // load the author avatar - PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(binding.authorAvatar); + CoilHelper.INSTANCE.loadAvatar(binding.authorAvatar, item.getUploaderAvatars()); binding.authorAvatar.setVisibility(ImageStrategy.shouldLoadImages() ? View.VISIBLE : View.GONE); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index ee6a0922c..8f0c3ac98 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -53,7 +53,7 @@ import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PlayButtonHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; -import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.util.text.TextEllipsizer; import java.util.ArrayList; @@ -62,6 +62,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.stream.Collectors; +import coil3.util.CoilUtils; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; @@ -71,8 +72,6 @@ import io.reactivex.rxjava3.disposables.Disposable; public class PlaylistFragment extends BaseListInfoFragment implements PlaylistControlViewHolder { - private static final String PICASSO_PLAYLIST_TAG = "PICASSO_PLAYLIST_TAG"; - private CompositeDisposable disposables; private Subscription bookmarkReactor; private AtomicBoolean isBookmarkButtonReady; @@ -271,7 +270,7 @@ public class PlaylistFragment extends BaseListInfoFragment() { +) : BindableItem() { companion object { const val PAYLOAD_SELECT = 1 @@ -21,34 +20,35 @@ class StreamSegmentItem( var isSelected = false - override fun bind(viewHolder: GroupieViewHolder, position: Int) { - item.previewUrl?.let { - PicassoHelper.loadThumbnail(it) - .into(viewHolder.root.findViewById(R.id.previewImage)) - } - viewHolder.root.findViewById(R.id.textViewTitle).text = item.title + override fun bind(viewBinding: ItemStreamSegmentBinding, position: Int) { + CoilHelper.loadThumbnail(viewBinding.previewImage, item.previewUrl) + viewBinding.textViewTitle.text = item.title if (item.channelName == null) { - viewHolder.root.findViewById(R.id.textViewChannel).visibility = View.GONE + viewBinding.textViewChannel.visibility = View.GONE // When the channel name is displayed there is less space // and thus the segment title needs to be only one line height. // But when there is no channel name displayed, the title can be two lines long. // The default maxLines value is set to 1 to display all elements in the AS preview, - viewHolder.root.findViewById(R.id.textViewTitle).maxLines = 2 + viewBinding.textViewTitle.maxLines = 2 } else { - viewHolder.root.findViewById(R.id.textViewChannel).text = item.channelName - viewHolder.root.findViewById(R.id.textViewChannel).visibility = View.VISIBLE + viewBinding.textViewChannel.text = item.channelName + viewBinding.textViewChannel.visibility = View.VISIBLE } - viewHolder.root.findViewById(R.id.textViewStartSeconds).text = + viewBinding.textViewStartSeconds.text = Localization.getDurationString(item.startTimeSeconds.toLong()) - viewHolder.root.setOnClickListener { onClick.onItemClick(this, item.startTimeSeconds) } - viewHolder.root.setOnLongClickListener { + viewBinding.root.setOnClickListener { onClick.onItemClick(this, item.startTimeSeconds) } + viewBinding.root.setOnLongClickListener { onClick.onItemLongClick(this, item.startTimeSeconds) true } - viewHolder.root.isSelected = isSelected + viewBinding.root.isSelected = isSelected } - override fun bind(viewHolder: GroupieViewHolder, position: Int, payloads: MutableList) { + override fun bind( + viewHolder: GroupieViewHolder, + position: Int, + payloads: MutableList + ) { if (payloads.contains(PAYLOAD_SELECT)) { viewHolder.root.isSelected = isSelected return @@ -57,4 +57,6 @@ class StreamSegmentItem( } override fun getLayout() = R.layout.item_stream_segment + + override fun initializeViewBinding(view: View) = ItemStreamSegmentBinding.bind(view) } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java index 7afc05c6c..92a5054e1 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java @@ -13,8 +13,8 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.image.CoilHelper; public class ChannelMiniInfoItemHolder extends InfoItemHolder { private final ImageView itemThumbnailView; @@ -56,7 +56,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder { itemAdditionalDetailView.setText(getDetailLine(item)); } - PicassoHelper.loadAvatar(item.getThumbnails()).into(itemThumbnailView); + CoilHelper.INSTANCE.loadAvatar(itemThumbnailView, item.getThumbnails()); itemView.setOnClickListener(view -> { if (itemBuilder.getOnChannelSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java index a19831cc7..5dee128eb 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java @@ -27,8 +27,8 @@ import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.text.TextEllipsizer; public class CommentInfoItemHolder extends InfoItemHolder { @@ -82,14 +82,12 @@ public class CommentInfoItemHolder extends InfoItemHolder { @Override public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof CommentsInfoItem)) { + if (!(infoItem instanceof CommentsInfoItem item)) { return; } - final CommentsInfoItem item = (CommentsInfoItem) infoItem; - // load the author avatar - PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); + CoilHelper.INSTANCE.loadAvatar(itemThumbnailView, item.getUploaderAvatars()); if (ImageStrategy.shouldLoadImages()) { itemThumbnailView.setVisibility(View.VISIBLE); itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java index c9216d9a9..b7949318d 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java @@ -9,8 +9,8 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.image.CoilHelper; public class PlaylistMiniInfoItemHolder extends InfoItemHolder { public final ImageView itemThumbnailView; @@ -46,7 +46,7 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder { .localizeStreamCountMini(itemStreamCountView.getContext(), item.getStreamCount())); itemUploaderView.setText(item.getUploaderName()); - PicassoHelper.loadPlaylistThumbnail(item.getThumbnails()).into(itemThumbnailView); + CoilHelper.INSTANCE.loadPlaylistThumbnail(itemThumbnailView, item.getThumbnails()); itemView.setOnClickListener(view -> { if (itemBuilder.getOnPlaylistSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java index 01f3be6b3..32fa8bf60 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java @@ -16,8 +16,8 @@ import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.DependentPreferenceHelper; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.StreamTypeUtil; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.views.AnimatedProgressBar; import java.util.concurrent.TimeUnit; @@ -87,7 +87,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - PicassoHelper.loadThumbnail(item.getThumbnails()).into(itemThumbnailView); + CoilHelper.INSTANCE.loadThumbnail(itemThumbnailView, item.getThumbnails()); itemView.setOnClickListener(view -> { if (itemBuilder.getOnStreamSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt b/app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt new file mode 100644 index 000000000..140351b0d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt @@ -0,0 +1,13 @@ +package org.schabi.newpipe.ktx + +import android.graphics.Bitmap +import android.graphics.Rect +import androidx.core.graphics.BitmapCompat + +@Suppress("NOTHING_TO_INLINE") +inline fun Bitmap.scale( + width: Int, + height: Int, + srcRect: Rect? = null, + scaleInLinearSpace: Boolean = true +) = BitmapCompat.createScaledBitmap(this, width, height, srcRect, scaleInLinearSpace) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt index e367961f1..258a67a4c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt @@ -21,7 +21,7 @@ import org.schabi.newpipe.extractor.stream.StreamType.POST_LIVE_STREAM import org.schabi.newpipe.extractor.stream.StreamType.VIDEO_STREAM import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.StreamTypeUtil -import org.schabi.newpipe.util.image.PicassoHelper +import org.schabi.newpipe.util.image.CoilHelper data class StreamItem( val streamWithState: StreamWithState, @@ -101,7 +101,7 @@ data class StreamItem( viewBinding.itemProgressView.visibility = View.GONE } - PicassoHelper.loadThumbnail(stream.thumbnailUrl).into(viewBinding.itemThumbnailView) + CoilHelper.loadThumbnail(viewBinding.itemThumbnailView, stream.thumbnailUrl) if (itemVersion != ItemVersion.MINI) { viewBinding.itemAdditionalDetails.text = diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt index aa03aafc5..11be80f2a 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt @@ -6,7 +6,6 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.Bitmap -import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build import android.provider.Settings @@ -17,20 +16,17 @@ import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.net.toUri import androidx.preference.PreferenceManager -import com.squareup.picasso.Picasso -import com.squareup.picasso.Target import org.schabi.newpipe.R import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.local.feed.service.FeedUpdateInfo import org.schabi.newpipe.util.NavigationHelper -import org.schabi.newpipe.util.image.PicassoHelper +import org.schabi.newpipe.util.image.CoilHelper /** * Helper for everything related to show notifications about new streams to the user. */ class NotificationHelper(val context: Context) { private val manager = NotificationManagerCompat.from(context) - private val iconLoadingTargets = ArrayList() /** * Show notifications for new streams from a single channel. The individual notifications are @@ -71,67 +67,34 @@ class NotificationHelper(val context: Context) { summaryBuilder.setStyle(style) // open the channel page when clicking on the summary notification + val intent = NavigationHelper + .getChannelIntent(context, data.serviceId, data.url) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) summaryBuilder.setContentIntent( - PendingIntentCompat.getActivity( - context, - data.pseudoId, - NavigationHelper - .getChannelIntent(context, data.serviceId, data.url) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - 0, - false - ) + PendingIntentCompat.getActivity(context, data.pseudoId, intent, 0, false) ) - // a Target is like a listener for image loading events - val target = object : Target { - override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { - // set channel icon only if there is actually one (for Android versions < 7.0) - summaryBuilder.setLargeIcon(bitmap) + val avatarIcon = + CoilHelper.loadBitmapBlocking(context, data.avatarUrl, R.drawable.ic_newpipe_triangle_white) + summaryBuilder.setLargeIcon(avatarIcon) - // Show individual stream notifications, set channel icon only if there is actually - // one - showStreamNotifications(newStreams, data.serviceId, data.url, bitmap) - // Show summary notification if enabled - if (manager.areNotificationsEnabled()) { - manager.notify(data.pseudoId, summaryBuilder.build()) - } - - iconLoadingTargets.remove(this) // allow it to be garbage-collected - } - - override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) { - // Show individual stream notifications - showStreamNotifications(newStreams, data.serviceId, data.url, null) - // Show summary notification if enabled - if (manager.areNotificationsEnabled()) { - manager.notify(data.pseudoId, summaryBuilder.build()) - } - iconLoadingTargets.remove(this) // allow it to be garbage-collected - } - - override fun onPrepareLoad(placeHolderDrawable: Drawable) { - // Nothing to do - } + // Show individual stream notifications, set channel icon only if there is actually one + showStreamNotifications(newStreams, data.serviceId, avatarIcon) + // Show summary notification + if (manager.areNotificationsEnabled()) { + manager.notify(data.pseudoId, summaryBuilder.build()) } - - // add the target to the list to hold a strong reference and prevent it from being garbage - // collected, since Picasso only holds weak references to targets - iconLoadingTargets.add(target) - - PicassoHelper.loadNotificationIcon(data.avatarUrl).into(target) } private fun showStreamNotifications( newStreams: List, serviceId: Int, - channelUrl: String, channelIcon: Bitmap? ) { if (manager.areNotificationsEnabled()) { newStreams.forEach { stream -> val notification = - createStreamNotification(stream, serviceId, channelUrl, channelIcon) + createStreamNotification(stream, serviceId, channelIcon) manager.notify(stream.url.hashCode(), notification) } } @@ -140,7 +103,6 @@ class NotificationHelper(val context: Context) { private fun createStreamNotification( item: StreamInfoItem, serviceId: Int, - channelUrl: String, channelIcon: Bitmap? ): Notification { return NotificationCompat.Builder( @@ -151,7 +113,7 @@ class NotificationHelper(val context: Context) { .setLargeIcon(channelIcon) .setContentTitle(item.name) .setContentText(item.uploaderName) - .setGroup(channelUrl) + .setGroup(item.uploaderUrl) .setColor(ContextCompat.getColor(context, R.color.ic_launcher_background)) .setColorized(true) .setAutoCancel(true) diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 528275d75..518fb3553 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -8,8 +8,8 @@ import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.image.CoilHelper; import java.time.format.DateTimeFormatter; @@ -30,17 +30,16 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistMetadataEntry)) { + if (!(localItem instanceof PlaylistMetadataEntry item)) { return; } - final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem; itemTitleView.setText(item.getOrderingName()); itemStreamCountView.setText(Localization.localizeStreamCountMini( itemStreamCountView.getContext(), item.getStreamCount())); itemUploaderView.setVisibility(View.INVISIBLE); - PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + CoilHelper.INSTANCE.loadPlaylistThumbnail(itemThumbnailView, item.getThumbnailUrl()); if (item instanceof PlaylistDuplicatesEntry && ((PlaylistDuplicatesEntry) item).getTimesStreamIsContained() > 0) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index 89a714fd7..7dc71bfb4 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -16,8 +16,8 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.DependentPreferenceHelper; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.views.AnimatedProgressBar; import java.time.format.DateTimeFormatter; @@ -83,8 +83,8 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - PicassoHelper.loadThumbnail(item.getStreamEntity().getThumbnailUrl()) - .into(itemThumbnailView); + CoilHelper.INSTANCE.loadThumbnail(itemThumbnailView, + item.getStreamEntity().getThumbnailUrl()); itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index 150a35eb5..f26a76ad9 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -16,8 +16,8 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.DependentPreferenceHelper; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.views.AnimatedProgressBar; import java.time.format.DateTimeFormatter; @@ -117,8 +117,8 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - PicassoHelper.loadThumbnail(item.getStreamEntity().getThumbnailUrl()) - .into(itemThumbnailView); + CoilHelper.INSTANCE.loadThumbnail(itemThumbnailView, + item.getStreamEntity().getThumbnailUrl()); itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index 3a339aec8..1eb97a59e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -8,8 +8,8 @@ import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; +import org.schabi.newpipe.util.image.CoilHelper; import java.time.format.DateTimeFormatter; @@ -29,10 +29,9 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistRemoteEntity)) { + if (!(localItem instanceof PlaylistRemoteEntity item)) { return; } - final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem; itemTitleView.setText(item.getOrderingName()); itemStreamCountView.setText(Localization.localizeStreamCountMini( @@ -45,7 +44,7 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { itemUploaderView.setText(ServiceHelper.getNameOfServiceById(item.getServiceId())); } - PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + CoilHelper.INSTANCE.loadPlaylistThumbnail(itemThumbnailView, item.getThumbnailUrl()); super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt index 7946de693..7687a7d6d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt @@ -9,7 +9,7 @@ import org.schabi.newpipe.R import org.schabi.newpipe.extractor.channel.ChannelInfoItem import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.OnClickGesture -import org.schabi.newpipe.util.image.PicassoHelper +import org.schabi.newpipe.util.image.CoilHelper class ChannelItem( private val infoItem: ChannelInfoItem, @@ -39,7 +39,7 @@ class ChannelItem( itemChannelDescriptionView.text = infoItem.description } - PicassoHelper.loadAvatar(infoItem.thumbnails).into(itemThumbnailView) + CoilHelper.loadAvatar(itemThumbnailView, infoItem.thumbnails) gesturesListener?.run { viewHolder.root.setOnClickListener { selected(infoItem) } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt index 3a4c6e41b..da35447e3 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt @@ -10,7 +10,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity import org.schabi.newpipe.databinding.PickerSubscriptionItemBinding import org.schabi.newpipe.ktx.AnimationType import org.schabi.newpipe.ktx.animate -import org.schabi.newpipe.util.image.PicassoHelper +import org.schabi.newpipe.util.image.CoilHelper data class PickerSubscriptionItem( val subscriptionEntity: SubscriptionEntity, @@ -21,7 +21,7 @@ data class PickerSubscriptionItem( override fun getSpanSize(spanCount: Int, position: Int): Int = 1 override fun bind(viewBinding: PickerSubscriptionItemBinding, position: Int) { - PicassoHelper.loadAvatar(subscriptionEntity.avatarUrl).into(viewBinding.thumbnailView) + CoilHelper.loadAvatar(viewBinding.thumbnailView, subscriptionEntity.avatarUrl) viewBinding.titleView.text = subscriptionEntity.name viewBinding.selectedHighlight.isVisible = isSelected } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index b07b15a45..159ecbd6e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -46,13 +46,14 @@ import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static coil3.Image_androidKt.toBitmap; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.support.v4.media.session.MediaSessionCompat; import android.util.Log; @@ -80,8 +81,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.video.VideoSize; -import com.squareup.picasso.Picasso; -import com.squareup.picasso.Target; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; @@ -126,13 +125,14 @@ import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.StreamTypeUtil; -import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.image.CoilHelper; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.IntStream; +import coil3.target.Target; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; @@ -181,7 +181,6 @@ public final class Player implements PlaybackListener, Listener { //////////////////////////////////////////////////////////////////////////*/ public static final int RENDERER_UNAVAILABLE = -1; - private static final String PICASSO_PLAYER_THUMBNAIL_TAG = "PICASSO_PLAYER_THUMBNAIL_TAG"; /*////////////////////////////////////////////////////////////////////////// // Playback @@ -200,6 +199,8 @@ public final class Player implements PlaybackListener, Listener { private MediaItemTag currentMetadata; @Nullable private Bitmap currentThumbnail; + @Nullable + private coil3.request.Disposable thumbnailDisposable; /*////////////////////////////////////////////////////////////////////////// // Player @@ -255,12 +256,6 @@ public final class Player implements PlaybackListener, Listener { @NonNull private final CompositeDisposable streamItemDisposable = new CompositeDisposable(); - // This is the only listener we need for thumbnail loading, since there is always at most only - // one thumbnail being loaded at a time. This field is also here to maintain a strong reference, - // which would otherwise be garbage collected since Picasso holds weak references to targets. - @NonNull - private final Target currentThumbnailTarget; - /*////////////////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////////////////*/ @@ -314,8 +309,6 @@ public final class Player implements PlaybackListener, Listener { videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver()); audioResolver = new AudioPlaybackResolver(context, dataSource); - currentThumbnailTarget = getCurrentThumbnailTarget(); - // The UIs added here should always be present. They will be initialized when the player // reaches the initialization step. Make sure the media session ui is before the // notification ui in the UIs list, since the notification depends on the media session in @@ -704,7 +697,6 @@ public final class Player implements PlaybackListener, Listener { databaseUpdateDisposable.clear(); progressUpdateDisposable.set(null); streamItemDisposable.clear(); - cancelLoadingCurrentThumbnail(); UIs.destroyAll(Object.class); // destroy every UI: obviously every UI extends Object } @@ -884,67 +876,58 @@ public final class Player implements PlaybackListener, Listener { //////////////////////////////////////////////////////////////////////////*/ //region Thumbnail loading - private Target getCurrentThumbnailTarget() { - // a Picasso target is just a listener for thumbnail loading events - return new Target() { - @Override - public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) { - if (DEBUG) { - Log.d(TAG, "Thumbnail - onBitmapLoaded() called with: bitmap = [" + bitmap - + " -> " + bitmap.getWidth() + "x" + bitmap.getHeight() + "], from = [" - + from + "]"); - } - // there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too. - onThumbnailLoaded(bitmap); - } - - @Override - public void onBitmapFailed(final Exception e, final Drawable errorDrawable) { - Log.e(TAG, "Thumbnail - onBitmapFailed() called", e); - // there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too. - onThumbnailLoaded(null); - } - - @Override - public void onPrepareLoad(final Drawable placeHolderDrawable) { - if (DEBUG) { - Log.d(TAG, "Thumbnail - onPrepareLoad() called"); - } - } - }; - } - private void loadCurrentThumbnail(final List thumbnails) { if (DEBUG) { Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with thumbnails = [" + thumbnails.size() + "]"); } - // first cancel any previous loading - cancelLoadingCurrentThumbnail(); + // Cancel any ongoing image loading + if (thumbnailDisposable != null) { + thumbnailDisposable.dispose(); + } // Unset currentThumbnail, since it is now outdated. This ensures it is not used in media - // session metadata while the new thumbnail is being loaded by Picasso. + // session metadata while the new thumbnail is being loaded by Coil. onThumbnailLoaded(null); if (thumbnails.isEmpty()) { return; } // scale down the notification thumbnail for performance - PicassoHelper.loadScaledDownThumbnail(context, thumbnails) - .tag(PICASSO_PLAYER_THUMBNAIL_TAG) - .into(currentThumbnailTarget); + final var thumbnailTarget = new Target() { + @Override + public void onError(@Nullable final coil3.Image error) { + Log.e(TAG, "Thumbnail - onError() called"); + // there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too. + onThumbnailLoaded(null); + } + + @Override + public void onStart(@Nullable final coil3.Image placeholder) { + if (DEBUG) { + Log.d(TAG, "Thumbnail - onStart() called"); + } + } + + @Override + public void onSuccess(@NonNull final coil3.Image result) { + if (DEBUG) { + Log.d(TAG, "Thumbnail - onSuccess() called with: drawable = [" + result + "]"); + } + // there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too. + onThumbnailLoaded(toBitmap(result)); + } + }; + thumbnailDisposable = CoilHelper.INSTANCE + .loadScaledDownThumbnail(context, thumbnails, thumbnailTarget); } - private void cancelLoadingCurrentThumbnail() { - // cancel the Picasso job associated with the player thumbnail, if any - PicassoHelper.cancelTag(PICASSO_PLAYER_THUMBNAIL_TAG); - } private void onThumbnailLoaded(@Nullable final Bitmap bitmap) { // Avoid useless thumbnail updates, if the thumbnail has not actually changed. Based on the // thumbnail loading code, this if would be skipped only when both bitmaps are `null`, since - // onThumbnailLoaded won't be called twice with the same nonnull bitmap by Picasso's target. + // onThumbnailLoaded won't be called twice with the same nonnull bitmap by Coil's target. if (currentThumbnail != bitmap) { currentThumbnail = bitmap; UIs.call(playerUi -> playerUi.onThumbnailLoaded(bitmap)); diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java index 066f92c26..8994aef79 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java @@ -6,8 +6,8 @@ import android.view.MotionEvent; import android.view.View; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; +import org.schabi.newpipe.util.image.CoilHelper; public class PlayQueueItemBuilder { private static final String TAG = PlayQueueItemBuilder.class.toString(); @@ -33,7 +33,7 @@ public class PlayQueueItemBuilder { holder.itemDurationView.setVisibility(View.GONE); } - PicassoHelper.loadThumbnail(item.getThumbnails()).into(holder.itemThumbnailView); + CoilHelper.INSTANCE.loadThumbnail(holder.itemThumbnailView, item.getThumbnails()); holder.itemRoot.setOnClickListener(view -> { if (onItemClickListener != null) { diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java index 09c61b8b3..d9e25fe8b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java @@ -13,8 +13,9 @@ import androidx.collection.SparseArrayCompat; import com.google.common.base.Stopwatch; +import org.schabi.newpipe.App; import org.schabi.newpipe.extractor.stream.Frameset; -import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.image.CoilHelper; import java.util.Comparator; import java.util.List; @@ -207,8 +208,8 @@ public class SeekbarPreviewThumbnailHolder { Log.d(TAG, "Downloading bitmap for seekbarPreview from '" + url + "'"); // Gets the bitmap within the timeout of 15 seconds imposed by default by OkHttpClient - // Ensure that your are not running on the main-Thread this will otherwise hang - final Bitmap bitmap = PicassoHelper.loadSeekbarThumbnailPreview(url).get(); + // Ensure that you are not running on the main thread, otherwise this will hang + final var bitmap = CoilHelper.INSTANCE.loadBitmapBlocking(App.getInstance(), url); if (sw != null) { Log.d(TAG, "Download of bitmap for seekbarPreview from '" + url + "' took " 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 b855f7c38..85ee97853 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -19,12 +19,12 @@ 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; import org.schabi.newpipe.util.image.PreferredImageQuality; -import java.io.IOException; import java.util.Locale; +import coil3.SingletonImageLoader; + public class ContentSettingsFragment extends BasePreferenceFragment { private String youtubeRestrictedModeEnabledKey; @@ -74,14 +74,12 @@ public class ContentSettingsFragment extends BasePreferenceFragment { (preference, newValue) -> { ImageStrategy.setPreferredImageQuality(PreferredImageQuality .fromPreferenceKey(requireContext(), (String) newValue)); - try { - PicassoHelper.clearCache(preference.getContext()); - Toast.makeText(preference.getContext(), - R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) + final var loader = SingletonImageLoader.get(preference.getContext()); + loader.getMemoryCache().clear(); + loader.getDiskCache().clear(); + Toast.makeText(preference.getContext(), + R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) .show(); - } catch (final IOException e) { - Log.e(TAG, "Unable to clear Picasso cache", e); - } return true; }); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 82f2f5bb6..229de7005 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -10,7 +10,6 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.feed.notifications.NotificationWorker; -import org.schabi.newpipe.util.image.PicassoHelper; import java.util.Optional; @@ -25,8 +24,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment { requirePreference(R.string.allow_heap_dumping_key); final Preference showMemoryLeaksPreference = requirePreference(R.string.show_memory_leaks_key); - final Preference showImageIndicatorsPreference = - requirePreference(R.string.show_image_indicators_key); final Preference checkNewStreamsPreference = requirePreference(R.string.check_new_streams_key); final Preference crashTheAppPreference = @@ -54,11 +51,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment { showMemoryLeaksPreference.setSummary(R.string.leak_canary_not_available); } - showImageIndicatorsPreference.setOnPreferenceChangeListener((preference, newValue) -> { - PicassoHelper.setIndicatorsEnabled((Boolean) newValue); - return true; - }); - checkNewStreamsPreference.setOnPreferenceClickListener(preference -> { NotificationWorker.runNow(preference.getContext()); return true; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index 18e0816bb..25d6b3a0f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -19,8 +19,8 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.local.subscription.SubscriptionManager; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.image.CoilHelper; import java.util.List; import java.util.Vector; @@ -190,7 +190,7 @@ public class SelectChannelFragment extends DialogFragment { final SubscriptionEntity entry = subscriptions.get(position); holder.titleView.setText(entry.getName()); holder.view.setOnClickListener(view -> clickedItem(position)); - PicassoHelper.loadAvatar(entry.getAvatarUrl()).into(holder.thumbnailView); + CoilHelper.INSTANCE.loadAvatar(holder.thumbnailView, entry.getAvatarUrl()); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index 880cbb282..ea475cb4f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -27,7 +27,7 @@ import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; -import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.image.CoilHelper; import java.util.List; import java.util.Vector; @@ -154,21 +154,17 @@ public class SelectPlaylistFragment extends DialogFragment { final int position) { final PlaylistLocalItem selectedItem = playlists.get(position); - if (selectedItem instanceof PlaylistMetadataEntry) { - final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); - + if (selectedItem instanceof PlaylistMetadataEntry entry) { holder.titleView.setText(entry.getOrderingName()); holder.view.setOnClickListener(view -> clickedItem(position)); - PicassoHelper.loadPlaylistThumbnail(entry.getThumbnailUrl()) - .into(holder.thumbnailView); - - } else if (selectedItem instanceof PlaylistRemoteEntity) { - final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + CoilHelper.INSTANCE.loadPlaylistThumbnail(holder.thumbnailView, + entry.getThumbnailUrl()); + } else if (selectedItem instanceof PlaylistRemoteEntity entry) { holder.titleView.setText(entry.getOrderingName()); holder.view.setOnClickListener(view -> clickedItem(position)); - PicassoHelper.loadPlaylistThumbnail(entry.getThumbnailUrl()) - .into(holder.thumbnailView); + CoilHelper.INSTANCE.loadPlaylistThumbnail(holder.thumbnailView, + entry.getThumbnailUrl()); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java index 9fe351b4b..d56362105 100644 --- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.util.external_communication; import static org.schabi.newpipe.MainActivity.DEBUG; +import static coil3.Image_androidKt.toBitmap; import android.content.ActivityNotFoundException; import android.content.ClipData; @@ -9,6 +10,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.text.TextUtils; @@ -25,12 +27,15 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.RouterActivity; import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; -import java.io.File; -import java.io.FileOutputStream; +import java.nio.file.Files; +import java.util.Collections; import java.util.List; +import coil3.SingletonImageLoader; +import coil3.disk.DiskCache; +import coil3.memory.MemoryCache; + public final class ShareUtils { private static final String TAG = ShareUtils.class.getSimpleName(); @@ -273,7 +278,7 @@ public final class ShareUtils { * @param content the content to share * @param images a set of possible {@link Image}s of the subject, among which to choose with * {@link ImageStrategy#choosePreferredImage(List)} since that's likely to - * provide an image that is in Picasso's cache + * provide an image that is in Coil's cache */ public static void shareText(@NonNull final Context context, @NonNull final String title, @@ -334,11 +339,9 @@ public final class ShareUtils { * *

* In order not to worry about network issues (timeouts, DNS issues, low connection speed, ...) - * when sharing a content, only images in the {@link com.squareup.picasso.LruCache LruCache} - * used by the Picasso library inside {@link PicassoHelper} are used as preview images. If the - * thumbnail image is not in the cache, no {@link ClipData} will be generated and {@code null} - * will be returned. - *

+ * when sharing a content, only images in the {@link MemoryCache} or {@link DiskCache} + * used by the Coil library are used as preview images. If the thumbnail image is not in the + * cache, no {@link ClipData} will be generated and {@code null} will be returned. * *

* In order to display the image in the content preview of the Android share sheet, an URI of @@ -354,12 +357,6 @@ public final class ShareUtils { *

* *

- * This method will call {@link PicassoHelper#getImageFromCacheIfPresent(String)} to get the - * thumbnail of the content in the {@link com.squareup.picasso.LruCache LruCache} used by - * the Picasso library inside {@link PicassoHelper}. - *

- * - *

* Using the result of this method when sharing has only an effect on the system share sheet (if * OEMs didn't change Android system standard behavior) on Android API 29 and higher. *

@@ -373,33 +370,46 @@ public final class ShareUtils { @NonNull final Context context, @NonNull final String thumbnailUrl) { try { - final Bitmap bitmap = PicassoHelper.getImageFromCacheIfPresent(thumbnailUrl); - if (bitmap == null) { - return null; - } - // Save the image in memory to the application's cache because we need a URI to the // image to generate a ClipData which will show the share sheet, and so an image file final Context applicationContext = context.getApplicationContext(); - final String appFolder = applicationContext.getCacheDir().getAbsolutePath(); - final File thumbnailPreviewFile = new File(appFolder - + "/android_share_sheet_image_preview.jpg"); + final var loader = SingletonImageLoader.get(context); + final var value = loader.getMemoryCache() + .get(new MemoryCache.Key(thumbnailUrl, Collections.emptyMap())); - // Any existing file will be overwritten with FileOutputStream - final FileOutputStream fileOutputStream = new FileOutputStream(thumbnailPreviewFile); - bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream); - fileOutputStream.close(); + final Bitmap cachedBitmap; + if (value != null) { + cachedBitmap = toBitmap(value.getImage()); + } else { + try (var snapshot = loader.getDiskCache().openSnapshot(thumbnailUrl)) { + if (snapshot != null) { + cachedBitmap = BitmapFactory.decodeFile(snapshot.getData().toString()); + } else { + cachedBitmap = null; + } + } + } + + if (cachedBitmap == null) { + return null; + } + + final var path = applicationContext.getCacheDir().toPath() + .resolve("android_share_sheet_image_preview.jpg"); + // Any existing file will be overwritten + try (var outputStream = Files.newOutputStream(path)) { + cachedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); + } final ClipData clipData = ClipData.newUri(applicationContext.getContentResolver(), "", - FileProvider.getUriForFile(applicationContext, - BuildConfig.APPLICATION_ID + ".provider", - thumbnailPreviewFile)); + FileProvider.getUriForFile(applicationContext, + BuildConfig.APPLICATION_ID + ".provider", + path.toFile())); if (DEBUG) { Log.d(TAG, "ClipData successfully generated for Android share sheet: " + clipData); } return clipData; - } catch (final Exception e) { Log.w(TAG, "Error when setting preview image for share sheet", e); return null; diff --git a/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt new file mode 100644 index 000000000..bd1c57f98 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt @@ -0,0 +1,185 @@ +package org.schabi.newpipe.util.image + +import android.content.Context +import android.graphics.Bitmap +import android.util.Log +import android.widget.ImageView +import androidx.annotation.DrawableRes +import coil3.executeBlocking +import coil3.imageLoader +import coil3.request.Disposable +import coil3.request.ImageRequest +import coil3.request.error +import coil3.request.placeholder +import coil3.request.target +import coil3.request.transformations +import coil3.size.Size +import coil3.target.Target +import coil3.toBitmap +import coil3.transform.Transformation +import kotlin.math.min +import org.schabi.newpipe.MainActivity +import org.schabi.newpipe.R +import org.schabi.newpipe.extractor.Image +import org.schabi.newpipe.ktx.scale + +object CoilHelper { + private val TAG = CoilHelper::class.java.simpleName + + @JvmOverloads + fun loadBitmapBlocking( + context: Context, + url: String?, + @DrawableRes placeholderResId: Int = 0 + ): Bitmap? = context.imageLoader + .executeBlocking(getImageRequest(context, url, placeholderResId).build()) + .image + ?.toBitmap() + + fun loadAvatar( + target: ImageView, + images: List + ) { + loadImageDefault(target, images, R.drawable.placeholder_person) + } + + fun loadAvatar( + target: ImageView, + url: String? + ) { + loadImageDefault(target, url, R.drawable.placeholder_person) + } + + fun loadThumbnail( + target: ImageView, + images: List + ) { + loadImageDefault(target, images, R.drawable.placeholder_thumbnail_video) + } + + fun loadThumbnail( + target: ImageView, + url: String? + ) { + loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video) + } + + fun loadScaledDownThumbnail( + context: Context, + images: List, + target: Target + ): Disposable { + val url = ImageStrategy.choosePreferredImage(images) + val request = + getImageRequest(context, url, R.drawable.placeholder_thumbnail_video) + .target(target) + .transformations( + object : Transformation() { + override val cacheKey = "COIL_PLAYER_THUMBNAIL_TRANSFORMATION_KEY" + + override suspend fun transform( + input: Bitmap, + size: Size + ): Bitmap { + if (MainActivity.DEBUG) { + Log.d(TAG, "Thumbnail - transform() called") + } + + val notificationThumbnailWidth = + min( + context.resources.getDimension(R.dimen.player_notification_thumbnail_width), + input.width.toFloat() + ).toInt() + + var newHeight = input.height / (input.width / notificationThumbnailWidth) + val result = input.scale(notificationThumbnailWidth, newHeight) + + return if (result == input || !result.isMutable) { + // create a new mutable bitmap to prevent strange crashes on some + // devices (see #4638) + newHeight = input.height / (input.width / (notificationThumbnailWidth - 1)) + input.scale(notificationThumbnailWidth, newHeight) + } else { + result + } + } + } + ).build() + + return context.imageLoader.enqueue(request) + } + + fun loadDetailsThumbnail( + target: ImageView, + images: List + ) { + val url = ImageStrategy.choosePreferredImage(images) + loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video, false) + } + + fun loadBanner( + target: ImageView, + images: List + ) { + loadImageDefault(target, images, R.drawable.placeholder_channel_banner) + } + + fun loadPlaylistThumbnail( + target: ImageView, + images: List + ) { + loadImageDefault(target, images, R.drawable.placeholder_thumbnail_playlist) + } + + fun loadPlaylistThumbnail( + target: ImageView, + url: String? + ) { + loadImageDefault(target, url, R.drawable.placeholder_thumbnail_playlist) + } + + private fun loadImageDefault( + target: ImageView, + images: List, + @DrawableRes placeholderResId: Int + ) { + loadImageDefault(target, ImageStrategy.choosePreferredImage(images), placeholderResId) + } + + private fun loadImageDefault( + target: ImageView, + url: String?, + @DrawableRes placeholderResId: Int, + showPlaceholder: Boolean = true + ) { + val request = + getImageRequest(target.context, url, placeholderResId, showPlaceholder) + .target(target) + .build() + target.context.imageLoader.enqueue(request) + } + + private fun getImageRequest( + context: Context, + url: String?, + @DrawableRes placeholderResId: Int, + showPlaceholderWhileLoading: Boolean = true + ): ImageRequest.Builder { + // if the URL was chosen with `choosePreferredImage` it will be null, but check again + // `shouldLoadImages` in case the URL was chosen with `imageListToDbUrl` (which is the case + // for URLs stored in the database) + val takenUrl = url?.takeIf { it.isNotEmpty() && ImageStrategy.shouldLoadImages() } + + return ImageRequest + .Builder(context) + .data(takenUrl) + .error(placeholderResId) + .memoryCacheKey(takenUrl) + .diskCacheKey(takenUrl) + .apply { + if (takenUrl != null || showPlaceholderWhileLoading) { + placeholder(placeholderResId) + } + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java deleted file mode 100644 index 4b116bdf9..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java +++ /dev/null @@ -1,224 +0,0 @@ -package org.schabi.newpipe.util.image; - -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; -import static org.schabi.newpipe.util.image.ImageStrategy.choosePreferredImage; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.graphics.BitmapCompat; - -import com.squareup.picasso.Cache; -import com.squareup.picasso.LruCache; -import com.squareup.picasso.OkHttp3Downloader; -import com.squareup.picasso.Picasso; -import com.squareup.picasso.RequestCreator; -import com.squareup.picasso.Transformation; - -import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.Image; - -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import okhttp3.OkHttpClient; - -public final class PicassoHelper { - private static final String TAG = PicassoHelper.class.getSimpleName(); - private static final String PLAYER_THUMBNAIL_TRANSFORMATION_KEY = - "PICASSO_PLAYER_THUMBNAIL_TRANSFORMATION_KEY"; - - private PicassoHelper() { - } - - private static Cache picassoCache; - private static OkHttpClient picassoDownloaderClient; - - // suppress because terminate() is called in App.onTerminate(), preventing leaks - @SuppressLint("StaticFieldLeak") - private static Picasso picassoInstance; - - - public static void init(final Context context) { - picassoCache = new LruCache(10 * 1024 * 1024); - picassoDownloaderClient = new OkHttpClient.Builder() - .cache(new okhttp3.Cache(new File(context.getExternalCacheDir(), "picasso"), - 50L * 1024L * 1024L)) - // this should already be the default timeout in OkHttp3, but just to be sure... - .callTimeout(15, TimeUnit.SECONDS) - .build(); - - picassoInstance = new Picasso.Builder(context) - .memoryCache(picassoCache) // memory cache - .downloader(new OkHttp3Downloader(picassoDownloaderClient)) // disk cache - .defaultBitmapConfig(Bitmap.Config.RGB_565) - .build(); - } - - public static void terminate() { - picassoCache = null; - picassoDownloaderClient = null; - - if (picassoInstance != null) { - picassoInstance.shutdown(); - picassoInstance = null; - } - } - - public static void clearCache(final Context context) throws IOException { - picassoInstance.shutdown(); - picassoCache.clear(); // clear memory cache - final okhttp3.Cache diskCache = picassoDownloaderClient.cache(); - if (diskCache != null) { - diskCache.delete(); // clear disk cache - } - init(context); - } - - public static void cancelTag(final Object tag) { - picassoInstance.cancelTag(tag); - } - - public static void setIndicatorsEnabled(final boolean enabled) { - picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging - } - - - public static RequestCreator loadAvatar(@NonNull final List images) { - return loadImageDefault(images, R.drawable.placeholder_person); - } - - public static RequestCreator loadAvatar(@Nullable final String url) { - return loadImageDefault(url, R.drawable.placeholder_person); - } - - public static RequestCreator loadThumbnail(@NonNull final List images) { - return loadImageDefault(images, R.drawable.placeholder_thumbnail_video); - } - - public static RequestCreator loadThumbnail(@Nullable final String url) { - return loadImageDefault(url, R.drawable.placeholder_thumbnail_video); - } - - public static RequestCreator loadDetailsThumbnail(@NonNull final List images) { - return loadImageDefault(choosePreferredImage(images), - R.drawable.placeholder_thumbnail_video, false); - } - - public static RequestCreator loadBanner(@NonNull final List images) { - return loadImageDefault(images, R.drawable.placeholder_channel_banner); - } - - public static RequestCreator loadPlaylistThumbnail(@NonNull final List images) { - return loadImageDefault(images, R.drawable.placeholder_thumbnail_playlist); - } - - public static RequestCreator loadPlaylistThumbnail(@Nullable final String url) { - return loadImageDefault(url, R.drawable.placeholder_thumbnail_playlist); - } - - public static RequestCreator loadSeekbarThumbnailPreview(@Nullable final String url) { - return picassoInstance.load(url); - } - - public static RequestCreator loadNotificationIcon(@Nullable final String url) { - return loadImageDefault(url, R.drawable.ic_newpipe_triangle_white); - } - - - public static RequestCreator loadScaledDownThumbnail(final Context context, - @NonNull final List images) { - // scale down the notification thumbnail for performance - return PicassoHelper.loadThumbnail(images) - .transform(new Transformation() { - @Override - public Bitmap transform(final Bitmap source) { - if (DEBUG) { - Log.d(TAG, "Thumbnail - transform() called"); - } - - final float notificationThumbnailWidth = Math.min( - context.getResources() - .getDimension(R.dimen.player_notification_thumbnail_width), - source.getWidth()); - - final Bitmap result = BitmapCompat.createScaledBitmap( - source, - (int) notificationThumbnailWidth, - (int) (source.getHeight() - / (source.getWidth() / notificationThumbnailWidth)), - null, - true); - - if (result == source || !result.isMutable()) { - // create a new mutable bitmap to prevent strange crashes on some - // devices (see #4638) - final Bitmap copied = BitmapCompat.createScaledBitmap( - source, - (int) notificationThumbnailWidth - 1, - (int) (source.getHeight() / (source.getWidth() - / (notificationThumbnailWidth - 1))), - null, - true); - source.recycle(); - return copied; - } else { - source.recycle(); - return result; - } - } - - @Override - public String key() { - return PLAYER_THUMBNAIL_TRANSFORMATION_KEY; - } - }); - } - - @Nullable - public static Bitmap getImageFromCacheIfPresent(@NonNull final String imageUrl) { - // URLs in the internal cache finish with \n so we need to add \n to image URLs - return picassoCache.get(imageUrl + "\n"); - } - - - private static RequestCreator loadImageDefault(@NonNull final List images, - @DrawableRes final int placeholderResId) { - return loadImageDefault(choosePreferredImage(images), placeholderResId); - } - - private static RequestCreator loadImageDefault(@Nullable final String url, - @DrawableRes final int placeholderResId) { - return loadImageDefault(url, placeholderResId, true); - } - - private static RequestCreator loadImageDefault(@Nullable final String url, - @DrawableRes final int placeholderResId, - final boolean showPlaceholderWhileLoading) { - // if the URL was chosen with `choosePreferredImage` it will be null, but check again - // `shouldLoadImages` in case the URL was chosen with `imageListToDbUrl` (which is the case - // for URLs stored in the database) - if (isNullOrEmpty(url) || !ImageStrategy.shouldLoadImages()) { - return picassoInstance - .load((String) null) - .placeholder(placeholderResId) // show placeholder when no image should load - .error(placeholderResId); - } else { - final RequestCreator requestCreator = picassoInstance - .load(url) - .error(placeholderResId); - if (showPlaceholderWhileLoading) { - requestCreator.placeholder(placeholderResId); - } - return requestCreator; - } - } -} diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index ab6e9e345..d8592b905 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -241,7 +241,6 @@ show_memory_leaks_key allow_disposed_exceptions_key show_original_time_ago_key - show_image_indicators_key show_crash_the_player_key check_new_streams crash_the_app_key diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index 84bb281f3..d97c5aa1a 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -34,13 +34,6 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> - - 2.8 is the last version, not 2.71828! -picasso = "2.8" preference = "1.2.1" prettytime = "5.0.8.Final" recyclerview = "1.4.0" @@ -91,6 +90,8 @@ androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "work" } androidx-work-rxjava3 = { module = "androidx.work:work-rxjava3", version.ref = "work" } assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" } +coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } +coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" } evernote-statesaver-compiler = { module = "com.evernote:android-state-processor", version.ref = "statesaver" } evernote-statesaver-core = { module = "com.evernote:android-state", version.ref = "statesaver" } facebook-stetho-core = { module = "com.facebook.stetho:stetho", version.ref = "stetho" } @@ -127,7 +128,6 @@ squareup-leakcanary-core = { module = "com.squareup.leakcanary:leakcanary-androi squareup-leakcanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakcanary" } squareup-leakcanary-watcher = { module = "com.squareup.leakcanary:leakcanary-object-watcher-android", version.ref = "leakcanary" } squareup-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } -squareup-picasso = { module = "com.squareup.picasso:picasso", version.ref = "picasso" } zacsweers-autoservice-compiler = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoservice-zacsweers" } [plugins] From 402a0aaaa383349a6091d3b65fc78e3f74a57cf9 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Sat, 21 Feb 2026 16:45:06 +0800 Subject: [PATCH 3/3] Delete unused preference title and summary Signed-off-by: Aayush Gupta --- app/src/main/res/values-ar-rLY/strings.xml | 2 -- app/src/main/res/values-ar/strings.xml | 2 -- app/src/main/res/values-az/strings.xml | 2 -- app/src/main/res/values-be/strings.xml | 2 -- app/src/main/res/values-bg/strings.xml | 2 -- app/src/main/res/values-bn/strings.xml | 1 - app/src/main/res/values-bs/strings.xml | 2 -- app/src/main/res/values-ca/strings.xml | 2 -- app/src/main/res/values-ckb/strings.xml | 2 -- app/src/main/res/values-cs/strings.xml | 2 -- app/src/main/res/values-da/strings.xml | 2 -- app/src/main/res/values-de/strings.xml | 2 -- app/src/main/res/values-el/strings.xml | 2 -- app/src/main/res/values-es/strings.xml | 2 -- app/src/main/res/values-et/strings.xml | 2 -- app/src/main/res/values-eu/strings.xml | 2 -- app/src/main/res/values-fa/strings.xml | 2 -- app/src/main/res/values-fi/strings.xml | 2 -- app/src/main/res/values-fr/strings.xml | 2 -- app/src/main/res/values-gl/strings.xml | 2 -- app/src/main/res/values-he/strings.xml | 2 -- app/src/main/res/values-hi/strings.xml | 2 -- app/src/main/res/values-hr/strings.xml | 2 -- app/src/main/res/values-hu/strings.xml | 2 -- app/src/main/res/values-in/strings.xml | 2 -- app/src/main/res/values-is/strings.xml | 2 -- app/src/main/res/values-it/strings.xml | 2 -- app/src/main/res/values-ja/strings.xml | 2 -- app/src/main/res/values-ka/strings.xml | 2 -- app/src/main/res/values-ko/strings.xml | 2 -- app/src/main/res/values-lt/strings.xml | 2 -- app/src/main/res/values-lv/strings.xml | 2 -- app/src/main/res/values-ml/strings.xml | 2 -- app/src/main/res/values-nb-rNO/strings.xml | 2 -- app/src/main/res/values-nl-rBE/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 2 -- app/src/main/res/values-nqo/strings.xml | 2 -- app/src/main/res/values-or/strings.xml | 2 -- app/src/main/res/values-pa/strings.xml | 2 -- app/src/main/res/values-pl/strings.xml | 2 -- app/src/main/res/values-pt-rBR/strings.xml | 2 -- app/src/main/res/values-pt-rPT/strings.xml | 2 -- app/src/main/res/values-pt/strings.xml | 2 -- app/src/main/res/values-ro/strings.xml | 2 -- app/src/main/res/values-ru/strings.xml | 2 -- app/src/main/res/values-ryu/strings.xml | 2 -- app/src/main/res/values-sat/strings.xml | 2 -- app/src/main/res/values-sc/strings.xml | 2 -- app/src/main/res/values-sk/strings.xml | 2 -- app/src/main/res/values-so/strings.xml | 2 -- app/src/main/res/values-sr/strings.xml | 2 -- app/src/main/res/values-sv/strings.xml | 2 -- app/src/main/res/values-ta/strings.xml | 2 -- app/src/main/res/values-te/strings.xml | 2 -- app/src/main/res/values-tr/strings.xml | 2 -- app/src/main/res/values-uk/strings.xml | 2 -- app/src/main/res/values-vi/strings.xml | 2 -- app/src/main/res/values-zh-rCN/strings.xml | 2 -- app/src/main/res/values-zh-rHK/strings.xml | 2 -- app/src/main/res/values-zh-rTW/strings.xml | 2 -- app/src/main/res/values/strings.xml | 2 -- 61 files changed, 120 deletions(-) diff --git a/app/src/main/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml index e481d6bf7..05e3813d5 100644 --- a/app/src/main/res/values-ar-rLY/strings.xml +++ b/app/src/main/res/values-ar-rLY/strings.xml @@ -299,7 +299,6 @@ %s مشارك جلب البيانات الوصفية… - إظهار مؤشرات الصور انقر للتنزيل %s تعطيل الوضع السريع , @@ -824,7 +823,6 @@ لقد اشتركت الآن في هذه القناة بدءًا من Android 10، يتم دعم \"Storage Access Framework\" فقط إنشاء اسم فريد - أظهر أشرطة ملونة لبيكاسو أعلى الصور تشير إلى مصدرها: الأحمر للشبكة والأزرق للقرص والأخضر للذاكرة فشل الاتصال الآمن يتوفر هذا الفيديو فقط لأعضاء YouTube Music Premium، لذلك لا يمكن بثه أو تنزيله من قبل NewPipe. البث السابق diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d4e1847f3..8a840f828 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -668,8 +668,6 @@ معاينة مصغرة على شريط التمرير وضع علامة على تمت مشاهدته أُعجب بها منشئ المحتوى - أظهر أشرطة ملونة لبيكاسو أعلى الصور تشير إلى مصدرها: الأحمر للشبكة والأزرق للقرص والأخضر للذاكرة - إظهار مؤشرات الصور اقتراحات البحث عن بعد اقتراحات البحث المحلية اسحب العناصر لإزالتها diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index aec305e72..73ed98d7e 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -490,8 +490,6 @@ Məlumat əldə edilir… Elementlərdə orijinal, əvvəlki vaxtı göstər Yaşam dövrəsi xaricindəki xətaları bildir - Şəkil göstəricilərini göstər - Şəkillərin üzərində mənbəsini göstərən Picasso rəngli lentləri göstər: şəbəkə üçün qırmızı, disk üçün mavi və yaddaş üçün yaşıl Bəzi endirmələri dayandırmaq mümkün olmasa da, mobil dataya keçərkən faydalıdır Bağla Fayl silindiyi üçün irəliləyiş itirildi diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 530d700a7..16c940e6c 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -611,8 +611,6 @@ Перайсці на вэб-сайт Каб выдаліць элемент, змахніце яго ўбок Прыбраць пастаянную мініяцюру - Паказваць на відарысах указальнікі - Паказваць на відарысах каляровыя меткі Picasso, якія абазначаюць яго крыніцу: чырвоная — сетка, сіняя — дыск, зялёная — памяць Апрацоўка стужкі… Пры кожным спампоўванні вам будзе прапанавана выбраць месца захавання Загрузка канала… diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 22f4b9685..87eee28d9 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -518,7 +518,6 @@ Това видео е с възрастова граница. \n \nВключете „%1$s“ в настройките ако искате да го пуснете. - Покажи цветни Picasso-панделки в горната част на изображенията като индикатор за техния произход (червен – от мрежата, син – от диска и червен – от паметта) Автоматична (тази на устройството) Мащабиране на миниатюрата в известието от 16:9 към 1:1 формат (възможни са изкривявания) Избете плейлист @@ -788,7 +787,6 @@ NewPipe откри грешка, докоснете, за да докладвате Няма потоци Деактивиране на медийното тунелиране - Покажи индикатори за изображения В очакване Неуспешна последваща обработка Прекъсване на мрежи с измерване diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 9059bd4f5..0d4fbd99c 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -584,7 +584,6 @@ নতুন ধারা কম্পাঙ্ক দেখো পূর্বদর্শন রেখার মাধ্যমে প্রাকদর্শন - ছবিরূপ সূচক দেখাও দেখিও না যেকোনো নেটওয়ার্ক পরেরটা ক্রমে রাখা হয়েছে diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 6c4b08a72..d422dbe91 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -449,8 +449,6 @@ Onemogući tuneliranje medija Onemogućite tuneliranje medija ako se pojavi crni ekran ili se prilikom reprodukcije videa pojavi prekid. Tuneliranje medija je onemogućeno prema zadanim postavkama na vašem uređaju jer je poznato da vaš model uređaja to ne podržava. - Prikaži indikatore slike - Prikažite Picasso obojene trake preko slika koje označavaju njihov izvor: crvena za mrežu, plava za disk i zelena za memoriju Prikaži \"Sruši plejer\" Prikazuje opciju pada sistema prilikom korištenja plejera Pokreni provjeru za nove tokove diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 88d5b2e65..64dd7a4a9 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -621,7 +621,6 @@ No mostris Baixa qualitat (més petit) Alta qualitat (més gran) - Mostra indicadors de la imatge Desactiva l\'entunelament del contingut si en reproduir el vídeos la pantalla se\'n va a negre o s\'entretallen. Mostra detalls del canal No s\'ha establert una carpeta de descàrregues, selecciona la carpeta per defecte ara @@ -665,7 +664,6 @@ Comentari fixat Mostra \"Força el tancament del reproductor\" Mostra una opció de fallada quan s\'utilitza el reproductor - Mostra les cintes de color Picasso a la part superior de les imatges que indiquen la seva font: vermell per a la xarxa, blau per al disc i verd per a la memòria El LeakCanary no està disponible Comprovant freqüència Es necesita una conexió a Internet diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index d168bbb37..23baa512c 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -625,8 +625,6 @@ پیشان نەدرێت کواڵێتی نزم (بچووکتر) کواڵێتی بەرز (گەورەتر) - پیشاندانی شریتە ڕەنگکراوەکانی پیکاسۆ لەسەرووی وێنەکانەوە بۆ بەدیار خستنی سەرچاوەکانیان : سوور بۆ تۆڕ ، شین بۆ دیسک و سەوز بۆ بیرگە - پیشاندانی دیارخەرەکانی وێنە پێشنیازکراوەکانی گەڕانی ڕیمۆت پێشنیازکراوەکانی گەڕانی نێوخۆیی دیارکردن وەک بینراو diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 37e8d70e5..283fb8a7d 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -648,8 +648,6 @@ %s stahování dokončena %s stahováních dokončeno - Zobrazit barevné pásky Picasso na obrázcích označujících jejich zdroj: červená pro síť, modrá pro disk a zelená pro paměť - Zobrazit indikátory obrázků Vzdálené návrhy vyhledávání Lokální návrhy vyhledávání Pokud je vypnuté automatické otáčení, nespouštět video v mini přehrávači, ale přepnout se přímo do režimu celé obrazovky. Do mini přehrávače se lze i nadále dostat ukončením režimu celé obrazovky diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 2be7c8bb8..7b14885e7 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -512,7 +512,6 @@ Behandler… Det kan tage et øjeblik Vis hukommelseslækager Deaktivér medietunneling - Vis billedindikatorer Netværkskrav Alle netværk Kontrolfrekvens @@ -689,7 +688,6 @@ Ofte stillede spørgsmål Hvis du har problemer med at bruge appen, bør du tjekke disse svar på almindelige spørgsmål! Se på hjemmesiden - Vis Picasso-farvede bånd oven på billeder, der angiver deres kilde: rød for netværk, blå for disk og grøn for hukommelse Du kører den nyeste version af NewPipe Grundet ExoPlayer-begrænsninger blev søgevarigheden sat til %d sekunder Vis kun ugrupperede abonnementer diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6270c658a..b19314f93 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -631,8 +631,6 @@ Aus Als gesehen markieren Vom Ersteller mit Herz versehen - Farbige Picasso-Bänder über den Bildern anzeigen, die deren Quelle angeben: rot für Netzwerk, blau für Festplatte und grün für Speicher - Bildindikatoren anzeigen Entfernte Suchvorschläge Lokale Suchvorschläge diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index b8166905e..17edecb59 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -628,8 +628,6 @@ Προεπισκόπηση στην μπάρα αναζήτησης Σήμανση ως αναπαραχθέν Επισημάνθηκε από τον δημιουργό - Εμφάνιση χρωματιστής κορδέλας πάνω στις εικόνες, που δείχνει την πηγή τους: κόκκινη για δίκτυο, μπλε για δίσκο και πράσινο για μνήμη - Εμφάνιση δεικτών εικόνων Προτάσεις απομακρυσμένης αναζήτησης Προτάσεις τοπικής αναζήτησης diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1841b6c09..051d90d34 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -631,8 +631,6 @@ Los comentarios están deshabilitados De corazón por el creador Marcar como visto - Mostrar cintas de colores Picasso encima de las imágenes indicando su origen: rojo para la red, azul para el disco y verde para la memoria - Mostrar indicadores de imagen Sugerencias de búsqueda remota Sugerencias de búsqueda local diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 5d7e829f3..66c7a7151 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -614,8 +614,6 @@ Südamlik autor Kas sinu meelest on voo laadimine aeglane? Sel juhul proovi lubada kiire laadimine (seda saad muuta seadetes või vajutades allolevat nuppu). \n \nNewPipe pakub kahte voo laadimise strateegiat: \n• Tellitud kanali täielik, kuid aeglane hankimine. \n• Teenuse spetsiaalse otspunkti kasutamine, mis on kiire, kuid tavaliselt mittetäielik. \n \nErinevus nende kahe vahel seisneb selles, et kiirel puudub tavaliselt teave, näiteks üksuse pikkus või tüüp (ei saa eristada reaalajas videoid tavalistest) ja see võib tagastada vähem üksusi. \n \nYouTube on näide teenusest, mis pakub seda kiirmeetodit oma RSS-vooga. \n \nNii et valik taandub sellele, mida eelistad: kiirus või täpne teave. Märgi vaadatuks - Näita piltide kohal Picasso värvides riba, mis märgib pildi allikat: punane tähistab võrku, sinine kohalikku andmekandjat ja roheline kohalikku mälu - Näita piltide allikat Kaugotsingu soovitused Kohaliku otsingu soovitused Üksuse eemaldamiseks viipa diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index a34a050fa..f48835460 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -616,8 +616,6 @@ Deskarga amaituta %s deskarga amaituta - Irudien gainean Picasso koloretako zintak erakutsi, jatorria adieraziz: gorria sarerako, urdina diskorako eta berdea memoriarako - Erakutsi irudi-adierazleak Urruneko bilaketa-iradokizunak Tokiko bilaketa-iradokizunak Ikusitako gisa markatu diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 09a01e0bd..70ea72938 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -592,7 +592,6 @@ رنگی کردن آگاهی گشودن با نشانه به عنوان دیده شده - نمایش روبان‌های رنگی پیکاسو در بالای تصویرها کهنشانگر منبعشان است: قرمز برای شبکه ، آبی برای دیسک و سبز برای حافظه درخواست از اندروید برای سفارشی‌سازی رنگ آگاهی براساس رنگ اصلی در بندانگشتی (توجّه داشته باشید که روی همهٔ افزاره‌ها در دسترس نیست) این ویدیو محدود به سن است. \n @@ -629,7 +628,6 @@ قلب‌شده به دست ایجادگر پیشنهادهای جست‌وجوی محلّی پیشنهادهای جست‌وجوی دوردست - نمایش نشانگرهای تصویر بارگیری پایان یافت %s بارگیری پایان یافتند diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 1ce4df009..ee470a3cc 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -628,8 +628,6 @@ Latauskansiota ei vielä asetettu, valitse ensin oletuslatauskansio Kommentit poistettu käytöstä Merkitse katsotuksi - Näytä Picasso-värjätyt nauhat kuvien päällä osoittaakseen lähteen: punainen tarkoittaa verkkoa, sininen tarkoittaa levytilaa ja vihreä tarkoittaa muistia - Näytä kuvailmaisimet Etähakuehdotukset Paikalliset hakuehdotukset Lisää seuraavaksi diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f7459e409..85ec148de 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -640,10 +640,8 @@ Prévisualisation de la barre de progression sur la miniature Marquer comme visionné Apprécié par le créateur - Afficher les indicateurs d’image Suggestions de recherche distante Suggestions de recherche locale - Affiche les rubans colorés de Picasso au-dessus des images indiquant leur source : rouge pour le réseau, bleu pour le disque et vert pour la mémoire %1$s téléchargement supprimé %1$s téléchargements supprimés diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index bea6c2eb7..c3873c2a4 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -621,7 +621,6 @@ %s descargas finalizadas Miniatura na barra de busca - Mostrar indicadores de imaxe Desactive o túnel multimedia se experimentar unha pantalla en negro ou interrupcións na reprodución. Desactivar túnel multimedia Engadido á cola @@ -658,7 +657,6 @@ A partir do Android 10, só o \'Sistema de Acceso ao Almacenamento\' está soportado Procesando... Pode devagar un momento Crear unha notificación de erro - Amosar fitas coloridas de Picasso na cima das imaxes que indican a súa fonte: vermello para a rede, azul para o disco e verde para a memoria Novos elementos Predefinido do ExoPlayer Amosar \"Travar o reprodutor\" diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 3d630394f..cfcf05e6d 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -648,8 +648,6 @@ תמונה מוקטנת בסרגל הנגינה סומן בלב על ידי היוצר סימון כנצפה - הצגת סרטים בסגנון פיקאסו בראש התמונות לציון המקור שלהם: אדום זה מהרשת, כחול מהכונן וירוק מהזיכרון - הצגת מחווני תמונות הצעות חיפוש מרוחקות הצעות חיפוש מקומיות diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 3a5cda8c9..eb04149c6 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -566,7 +566,6 @@ कतारबद्ध हुआ स्ट्रीम विवरण लोड हो रहे हैं… प्रोसेस हो रहा है… कुछ समय लग सकता है - छवि संकेतक दिखाएं प्लेयर का उपयोग करते समय क्रैश विकल्प दिखाता है नई स्ट्रीमों के लिए जांच चलाएं एक त्रुटि स्नैकबार दिखाएं @@ -663,7 +662,6 @@ लीक-कैनरी उपलब्ध नहीं है एक त्रुटी हुई है, नोटीफिकेशन देखें यदि वीडियो प्लेबैक पर आप काली स्क्रीन या रुक-रुक कर वीडियो चलने का अनुभव करते हैं तो मीडिया टनलिंग को अक्षम करें। - छवियों के शीर्ष पर पिकासो रंगीन रिबन दिखाएँ जो उनके स्रोत को दर्शाता है: नेटवर्क के लिए लाल, डिस्क के लिए नीला और मेमोरी के लिए हरा त्रुटी की नोटीफिकेशन बनाएं इस डाउनलोड को पुनर्प्राप्त नहीं किया जा सकता अभी तक कोई डाउनलोड फ़ोल्डर सेट नहीं किया गया है, अब डिफ़ॉल्ट डाउनलोड फ़ोल्डर चुनें diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index f9f95db72..28412795e 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -640,7 +640,6 @@ Sada možeš odabrati tekst u opisu. Napomena: stranica će možda treperiti i možda nećeš moći kliknuti poveznice u načinu rada za odabir teksta. Obrada u tijeku … Može malo potrajati Za ukljanjanje stavki povuci ih - Prikaži indikatore slike %s preuzimanje je gotovo %s preuzimanja su gotova @@ -649,7 +648,6 @@ Pokreni glavni player u cjeloekranskom prikazu Dodaj u popis kao sljedeći Dodano u popis kao sljedeći - Prikaži Picassove vrpce u boji na slikama koje označavaju njihov izvor: crvena za mrežu, plava za disk i zelena za memoriju Izbrisano %1$s preuzimanje Izbrisana %1$s preuzimanja diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 1110fd248..691e31567 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -550,7 +550,6 @@ Az eltávolítás utáni, fragment vagy activity életcikluson kívüli, nem kézbesíthető Rx kivételek jelentésének kényszerítése Eredeti „ennyi ideje” megjelenítése az elemeken Tiltsa le a médiacsatornázást, ha fekete képernyőt vagy akadozást tapasztal videólejátszáskor. - Picasso színes szalagok megjelenítése a képek fölött, megjelölve a forrásukat: piros a hálózathoz, kék a lemezhez, zöld a memóriához Minden letöltésnél meg fogja kérdezni, hogy hova mentse el Válasszon egy példányt Hírfolyam utoljára frissítve: %s @@ -624,7 +623,6 @@ A lejátszási listához való hozzáadás előtt és után megtekintett közvetítések el lesznek távolítva.\nBiztos benne? A szolgáltatásokból származó eredeti szövegek láthatók lesznek a közvetítési elemeken Lejátszó összeomlasztása - Képjelölők megjelenítése A „Lejátszó összeomlasztása” lehetőség megjelenítése Megjeleníti az összeomlasztási lehetőséget a lejátszó használatakor Hangmagasság megtartása (torzítást okozhat) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index d023e9fb3..eafd00e8d 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -625,13 +625,11 @@ Disukai oleh kreator Saran pencarian lokal Saran pencarian remote - Tampilkan indikator gambar Menghapus %1$s unduhan tambahkan ke selanjutnya telah ditambahkan ke selanjutnya - Tampilkan Ribon bewarna Picasso di atas gambar yang mengindikasikan asalnya: merah untuk jaringan, biru untuk disk dan hijau untuk memori Jangan memulai memutar video di mini player, tapi nyalakan langsung di mode layar penuh, jika rotasi otomatis terkunci. Anda tetap dapat mengakses mini player dengan keluar dari layar penuh Memproses… Mungkin butuh waktu sebentar Periksa Pembaruan diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 029c411ef..aa8c2e72c 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -637,8 +637,6 @@ Upprunalegir textar frá þjónustu verða sýnilegir í streymisatriðum Slökkva á margmiðlagöngum Slökktu á margmiðlunargöngum (media tunneling) ef vart verður við svartan skjá eða hökt við spilun myndskeiða. - Sýna myndvísa - Sýna Picasso litaða borða ofan á myndum sem gefa til kynna uppruna þeirra: rauðan fyrir netið, bláan fyrir disk og grænan fyrir minni Sýna „Láta spilara hrynja\" Sýna valkost til að hrynja spilara Hrynja forrit diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3a3995b14..c3e513079 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -638,8 +638,6 @@ Commenti disattivati Apprezzato dall\'autore Segna come visto - Mostra gli indicatori colorati Picasso sopra le immagini, per indicare la loro fonte: rosso per la rete, blu per il disco e verde per la memoria - Mostra indicatori immagine Suggerimenti di ricerca remoti Suggerimenti di ricerca locali diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ac9fe3684..b0be85292 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -628,8 +628,6 @@ %s 個のダウンロードが完了しました - ピカソは、画像の上に、画像の出所を識別する色彩記章を表示します: 赤はネットワーク、青はディスク、緑はメモリ - 画像に標識を表示 処理中… 少し時間がかかるかもしれません 新しいバージョンを手動で確認します アップデートを確認中… diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 444625fc0..98b5e2475 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -380,7 +380,6 @@ ორიგინალური ტექსტები სერვისებიდან ხილული იქნება ნაკადის ერთეულებში მედია გვირაბის გათიშვა გამორთეთ მედია გვირაბი, თუ ვიდეოს დაკვრისას შავი ეკრანი ან ჭუჭყი გაქვთ - გამოსახულების ინდიკატორების ჩვენება აჩვენე \"დამკვრელის დამსხვრევა\" აჩვენებს ავარიის ვარიანტს დამკვრელის გამოყენებისას გაუშვით შემოწმება ახალი ნაკადებისთვის @@ -677,7 +676,6 @@ გამოწერების იმპორტი ვერ მოხერხდა შეატყობინეთ სასიცოცხლო ციკლის შეცდომებს იძულებითი მოხსენება შეუსაბამო Rx გამონაკლისების შესახებ ფრაგმენტის ან აქტივობის სასიცოცხლო ციკლის გარეთ განკარგვის შემდეგ - აჩვენეთ პიკასოს ფერადი ლენტები სურათების თავზე, სადაც მითითებულია მათი წყარო: წითელი ქსელისთვის, ლურჯი დისკისთვის და მწვანე მეხსიერებისთვის სწრაფი კვების რეჟიმი ამაზე მეტ ინფორმაციას არ იძლევა. „%s“-ის არხის ჩატვირთვა ვერ მოხერხდა. ხელმისაწვდომია ზოგიერთ სერვისში, როგორც წესი, ბევრად უფრო სწრაფია, მაგრამ შეიძლება დააბრუნოს შეზღუდული რაოდენობის ელემენტი და ხშირად არასრული ინფორმაცია (მაგ. ხანგრძლივობის გარეშე, ელემენტის ტიპი, არ არის ლაივის სტატუსი) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 59832ece4..079dc45b4 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -541,8 +541,6 @@ 미디어 터널링 비활성화 서비스의 원본 텍스트가 스트림 항목에 표시됩니다 동영상 재생 시 검은 화면이 나타나거나 끊김 현상이 발생하면 미디어 터널링을 비활성화하세요. - 이미지 표시기 표시 - 원본을 나타내는 이미지 위에 피카소 컬러 리본 표시: 네트워크는 빨간색, 디스크는 파란색, 메모리는 녹색 \"플레이어 충돌\" 표시 플레이어를 사용할 때 충돌 옵션을 표시합니다 새로운 스트림 확인 실행 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index dd7874809..d43a69fb0 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -638,8 +638,6 @@ Nerodyti Širdelė nuo kurėjo Pažymėti kaip peržiūrėtą - Rodyti „Picasso“ spalvotas juosteles ant vaizdų, nurodančių jų šaltinį: raudona tinklui, mėlyna diskui ir žalia atmintis - Rodyti vaizdo indikatorius Nuotolinės paieškos pasiūlymai Vietinės paieškos pasiūlymai diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 45d126183..e474f606c 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -612,12 +612,10 @@ Nesākt video atskaņošanu samazinātā režīmā, bet pilnekrāna režīmā, ja automātiskā rotācija ir izslēgta Izslēgt multivides tuneļošanu Izslēdziet multivides tuneļošanu, ja jums video atskaņošanas laikā parādās melns ekrāns vai aizķeršanās. - Rādīt krāsainas lentes virs attēliem, norādot to avotu: sarkana - tīkls, zila - disks, zaļa - atmiņa Ieslēgt teksta atlasīšanu video aprakstā Lejupielādes mape vēl nav iestatīta, izvēlieties noklusējuma lejupielādes mapi Pavelciet atlasīto elementu pa kreisi vai labi, lai to aizvāktu Lokālie meklēšanas ieteikumi - Rādīt attēlu indikatorus Augstas kvalitātes (lielāks) Pārbaudīt atjauninājumus Pašrocīgi pārbaudīt jaunas versijas pieejamību diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index a149b910b..013802176 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -590,7 +590,6 @@ രണ്ടാം പ്രവർത്തന ബട്ടൺ ആദ്യ പ്രവർത്തന ബട്ടൺ വീഡിയോ കാണുമ്പോൾ കറുത്ത സ്ക്രീൻ, അവ്യക്തത അനുഭവിക്കുന്നു എങ്കിൽ മീഡിയ ട്യൂൺലിങ് പ്രവർത്തനരഹിതമാക്കുക - ഉറവിടം തിരിച്ചറിയാൻ പിക്കാസോ കളർഡ് റിബൺ ചിത്രങ്ങളുടെ മുകളിൽ കാണിക്കുക: നെറ്റ്‌വർക്കിന് ചുവപ്പ്, ഡിസ്കിനു നീല, മെമ്മറിയിക്ക് പച്ച സീക്ബാർ ചെറുചിത്രം പ്രദർശനം സ്നേഹത്തോടെ സൃഷ്ടാവ് ഡിസ്ക്രിപ്ഷനിലെ ടെക്സ്റ്റ്‌ സെലക്ട്‌ ചെയ്യുവാൻ അനുവദിക്കാതെ ഇരിക്കുക @@ -624,7 +623,6 @@ കാണിക്കരുത് കുറഞ്ഞ നിലവാരം (ചെറുത് ) ഉയർന്ന നിലവാരം (വലിയത് ) - ഇമേജ് ഇൻഡിക്കേറ്ററുകൾ കാണിക്കുക മീഡിയ ട്യൂൺലിങ് പ്രവർത്തനരഹിതമാക്കുക ഡൌൺലോഡ് ഫോൾഡർ ഇത് വരെയും സെറ്റ് ചെയ്തിട്ടില്ല, സ്ഥിര ഡൌൺലോഡ് ഫോൾഡർ ഇപ്പോൾ തിരഞ്ഞെക്കുക അഭിപ്രായങ്ങൾ പ്രവർത്തനരഹിതമായിരിക്കുന്നു diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 633140dcc..7d25bf847 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -638,9 +638,7 @@ Lokale søkeforslag Marker som sett Ikke start videoer i minispilleren, men bytt til fullskjermsmodus direkte dersom auto-rotering er låst. Du har fremdeles tilgang til minispilleren ved å avslutte fullskjermsvisningen - Vis Picasso-fargede bånd på toppen av bilder for å indikere kilde: Rød for nettverk, blå for disk, og grønn for minne Hjertemerket av skaperen - Vis bildeindikatorer Dra elementer for å fjerne dem Start hovedspiller i fullskjerm Still i kø neste diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml index 92d36ab77..237e9238c 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -606,7 +606,6 @@ Geen download map ingesteld, kies nu de standaard download map Niet tonen Reacties zijn uitgeschakeld - Toon afbeeldingsindicatoren Speler melding Configureer actieve stream melding Meldingen diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 845e7431c..2ff13b391 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -626,8 +626,6 @@ Lage kwaliteit (kleiner) Hoge kwaliteit (groter) Zoekbalk miniatuurafbeelding voorbeeld - Toon Picasso-gekleurde linten bovenop afbeeldingen die hun bron aangeven: rood voor netwerk, blauw voor schijf en groen voor geheugen - Afbeeldings­indicatoren tonen Reacties zijn uitgeschakeld Zoeksuggesties op afstand Lokale zoeksuggesties diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index abf7ad70d..ee17b0921 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -436,8 +436,6 @@ ߗߋߢߊߟߌ ߟߎ߬ ߞߟߏߜߍ߫ ߓߐߛߎ߲ߡߊ ߟߎ߬ ߦߌ߬ߘߊ߬ߕߐ߫ ߟߋ߬ ߟߊ߬ߖߍ߲߬ߛߍ߲߬ߠߌ߲ ߘߐ߫ ߞߋߟߋߞߋߟߋ ߟߊ߫ ߝߊߟߊ߲ߓߍߦߊ ߟߊߛߊ߬ ߞߋߟߋߞߋߟߋ ߟߊ߫ ߝߊߟߊ߲ߓߍߦߊ ߟߊߛߊ߬ ߣߴߌ ߞߊ߬ ߥߊ߲߬ߊߥߊ߲߬ ߝߌ߲ ߦߋ߫ ߥߟߊ߫ ߜߊߘߊ߲ߜߊߘߊ߲ߠߌ߲ ߦߋߡߍ߲ߕߊ ߘߏ߫ ߘߐߛߊߙߌ߫ ߕߎߡߊ - ߞߊ߬ ߖߌ߬ߦߊ߬ߓߍ߫ ߦߌ߬ߘߊ߬ߟߊ߲ ߠߎ߫ ߝߍ߲߬ߛߍ߲߫ - ߏ߬ ߦߋ߫ ߔߌߛߊߞߏ߫ ߟߊ߫ ߡߙߎߝߋ߫ ߞߟߐ߬ߡߊ ߟߎ߫ ߟߋ߬ ߝߍ߲߬ߛߍ߲߬ ߠߊ߫ ߖߌ߬ߦߊ߬ߓߍ ߟߎ߫ ߞߎ߲߬ߘߐ߫ ߞߵߊ߬ߟߎ߬ ߓߐߛߎ߲ ߦߌ߬ߘߊ߬: ߥߎߟߋ߲߬ߡߊ߲ ߦߋ߫ ߞߙߏ߬ߝߏ ߕߊ ߘߌ߫߸ ߓߊ߯ߡߊ ߦߋ߫ ߝߘߍ߬ ߜߍߟߍ߲ ߕߊ ߘߌ߫ ߊ߬ߣߌ߫ ߝߙߌߛߌߡߊ ߦߋ߫ ߦߟߌߕߏߟߊ߲ ߕߊ ߘߌ߫ ߟߊ߬ߓߐ߬ߟߌ ߦߴߌߘߐ߫… ߞߵߊ߬ ߘߊߡߌ߬ߣߊ߬ ߞߊ߲߬ߞߎߡߊ ߟߎ߬ ߟߊߛߊ߬ߣߍ߲ ߠߋ߬ diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index c9d0e6758..57459de10 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -340,7 +340,6 @@ ସେବାଗୁଡିକରୁ ମୂଳ ଲେଖା ଷ୍ଟ୍ରିମ୍ ଆଇଟମ୍ ଗୁଡିକରେ ଦୃଶ୍ୟମାନ ହେବ ମିଡିଆ ଟନେଲିଂକୁ ଅକ୍ଷମ କରନ୍ତୁ ଯଦି ଆପଣ ଏକ କଳା ପରଦା ଅନୁଭବ କରନ୍ତି କିମ୍ବା ଭିଡିଓ ପ୍ଲେବେକ୍ ଉପରେ ଝୁଣ୍ଟି ପଡ଼ନ୍ତି ତେବେ ମିଡିଆ ଟନେଲିଂକୁ ଅକ୍ଷମ କରନ୍ତୁ । - ଚିତ୍ରଗୁଡ଼ିକର ଉପରେ ପିକାସୋ ରଙ୍ଗୀନ ଫିତା ଦେଖାନ୍ତୁ: ସେମାନଙ୍କର ଉତ୍ସକୁ ସୂଚାଇଥାଏ: ନେଟୱାର୍କ ପାଇଁ ନାଲି, ଡିସ୍କ ପାଇଁ ନୀଳ ଏବଂ ସ୍ମୃତି ପାଇଁ ସବୁଜ ଆମଦାନି କରନ୍ତୁ ଠାରୁ ଆମଦାନୀ କରନ୍ତୁ ଆମଦାନି… @@ -651,7 +650,6 @@ ଅଟୋ-ଜେନେରେଟ୍ (କୌଣସି ଅପଲୋଡର୍ ମିଳିଲା ନାହିଁ) ପୁରଣ କରନ୍ତୁ କ୍ୟାପସନ୍ - ପ୍ରତିଛବି ସୂଚକ ଦେଖାନ୍ତୁ ପ୍ଲେୟାର ବ୍ୟବହାର କରିବା ସମୟରେ ଏକ କ୍ରାସ୍ ବିକଳ୍ପ ଦେଖାଏ ନୂତନ ଷ୍ଟ୍ରିମ୍ ପାଇଁ ଯାଞ୍ଚ ଚଲାନ୍ତୁ ଆପ୍ କ୍ରାସ୍ କରନ୍ତୁ diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 8c69dd850..cc0be8ae4 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -602,8 +602,6 @@ ਨਿਊਪਾਈਪ ਖਾਮੀ ਤੋਂ ਪ੍ਰਭਾਵਤ ਹੋਈ ਹੈ, ਇੱਥੇ ਨੱਪ ਕੇ ਰਿਪੋਰਟ ਕਰੋ ਇੱਕ ਖਾਮੀ ਪ੍ਰਭਾਵੀ ਹੋਈ ਹੈ, ਨੋਟੀਫੀਕੇਸ਼ਨ ਵੇਖੋ ਆਈਟਮਾਂ ਨੂੰ ਇੱਕ ਪਾਸੇ ਖਿੱਚ ਕੇ ਹਟਾਓ - ਦ੍ਰਿਸ਼ ਸੂਚਕ ਵਿਖਾਓ - ਦ੍ਰਿਸ਼ਾਂ ਦੇ ਉੱਪਰ ਉਹਨਾਂ ਦੀ ਸਰੋਤ-ਪਛਾਣ ਲਈ ਪਿਕਾਸੋ ਦੇ ਰੰਗਦਾਰ ਰਿਬਨ ਵਿਖਾਓ : ਨੈੱਟਵਰਕ ਲਈ ਲਾਲ, ਡਿਸਕ ਲਈ ਨੀਲੇ ਤੇ ਮੈਮਰੀ ਲਈ ਹਰੇ ਨਵੀਂ ਸਟ੍ਰੀਮ ਦੇ ਨੋਟੀਫਿਕੇਸ਼ਨ ਪਿੰਨ ਕੀਤੀ ਟਿੱਪਣੀ ਅੱਪਡੇਟ ਦੀ ਉਪਲੱਬਧਤਾ ਪਰਖੀ ਜਾ ਰਹੀ… diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 910b11044..60dab077d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -643,8 +643,6 @@ Nie pokazuj Serduszko od twórcy Oznacz jako obejrzane - Pokazuj kolorowe wstążki Picasso nad obrazami wskazujące ich źródło: czerwone dla sieci, niebieskie dla dysku i zielone dla pamięci - Pokazuj wskaźniki obrazu Zdalne podpowiedzi wyszukiwania Lokalne podpowiedzi wyszukiwania diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1cbfa6947..595fa9613 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -638,7 +638,6 @@ Os comentários estão desabilitados Marcar como assistido Curtido pelo criador - Exibir fitas coloridas no topo das imagens indicando sua fonte: vermelho para rede, azul para disco e verde para memória %1$s download excluído %1$s downloads excluídos @@ -649,7 +648,6 @@ %s downloads concluídos %s downloads concluídos - Mostrar indicadores de imagem Adicionado na próxima posição da fila Enfileira a próxima Deslize os itens para remove-los diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index f6e1b5a67..f14d2e402 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -638,8 +638,6 @@ Ainda não foi definida uma pasta de descarregamento, escolha agora a pasta de descarregamento padrão Comentários estão desativados Marcar como visto - Mostrar fitas coloridas de Picasso em cima das imagens que indicam a sua fonte: vermelho para rede, azul para disco e verde para memória - Mostrar indicadores de imagem Sugestões de pesquisa remotas Sugestões de pesquisa locais diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 8e0210dbc..b3d8fe7b4 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -638,8 +638,6 @@ Baixa qualidade (menor) Alta qualidade (maior) Os comentários estão desativados - Mostrar fitas coloridas de Picasso em cima das imagens que indicam a sua fonte: vermelho para rede, azul para disco e verde para memória - Mostrar indicadores de imagem Sugestões de pesquisa remotas Sugestões de pesquisa locais diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2afe73875..c5c450894 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -654,8 +654,6 @@ Calitate scăzută (mai mică) Calitate înaltă (mai mare) Miniatură de previzualizare în bara de derulare - Afișați panglici colorate de Picasso deasupra imaginilor, indicând sursa acestora: roșu pentru rețea, albastru pentru disc și verde pentru memorie - Afișați indicatorii de imagine Dezactivați tunelarea media dacă întâmpinați un ecran negru sau blocaje la redarea video. Procesarea.. Poate dura un moment Verifică dacă există actualizări diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fc32c0da3..b3f92f167 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -646,8 +646,6 @@ Миниатюра над полосой прокрутки Автору видео понравилось это Пометить проигранным - Picasso: указать цветом источник изображений (красный — сеть, синий — диск, зелёный — память) - Цветные метки на изображениях Серверные предложения поиска Локальные предложения поиска diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml index 4be7d5328..f5b40be83 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -640,8 +640,6 @@ %sちぬダウンロードぬかんりょうさびたん %sちぬダウンロードぬかんりょうさびたん - ピカソー、がぞうぬういに、がぞうくとぅどぅくるしーきびちするしきさいきしーょうひょうじさびーん: あかーネットワーク、あおーディスク、みどぅれーメモリ - やしがぞうんかいふぃいょうしきひょうじ しーょりちゅう… くーてーんじがんがかかいんかむしりやびらん みーさるバージョンしーゅどうでぃかくにんさびーん アップデートかくにんちゅう… diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index 71a8067b1..47ac2c53d 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -268,7 +268,6 @@ LeakCanary ᱵᱟᱭ ᱧᱟᱢᱚᱜ ᱠᱟᱱᱟ ᱢᱮᱢᱚᱨᱤ ᱞᱤᱠᱟᱞ ᱢᱚᱱᱤᱴᱚᱨᱤᱝ ᱦᱤᱯ ᱰᱟᱢᱯᱤᱝ ᱚᱠᱛᱚ ᱨᱮ ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱨᱟᱥᱴᱨᱤᱭ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱡᱤᱭᱚᱱ ᱪᱤᱠᱤ ᱠᱷᱚᱱ ᱵᱟᱦᱨᱮ ᱨᱮ ᱵᱷᱮᱜᱟᱨ ᱠᱚ ᱚᱱᱚᱞ ᱢᱮ - ᱱᱮᱴᱣᱟᱨᱠ ᱞᱟᱹᱜᱤᱫ red, ᱰᱤᱥᱠ ᱞᱟᱹᱜᱤᱫ blue ᱟᱨ ᱢᱮᱢᱚᱨᱤ ᱞᱟᱹᱜᱤᱫ green ᱯᱞᱮᱭᱟᱨ ᱵᱮᱵᱷᱟᱨ ᱚᱠᱛᱮ ᱨᱮ ᱠᱨᱟᱥ ᱚᱯᱥᱚᱱ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ ᱤᱢᱯᱳᱨᱴ ᱤᱢᱯᱚᱨᱴ @@ -535,7 +534,6 @@ ᱡᱤᱱᱤᱥ ᱠᱚᱨᱮᱱᱟᱜ ᱢᱩᱞ ᱚᱠᱛᱚ ᱧᱮᱞ ᱢᱮ ᱥᱮᱵᱟ ᱠᱷᱚᱱ ᱚᱨᱡᱤᱱᱤᱭᱟᱞ ᱴᱮᱠᱥᱴ ᱠᱚ ᱥᱴᱨᱤᱢ ᱤᱴᱮᱢ ᱨᱮ ᱧᱮᱞᱚᱜᱼᱟ ᱡᱩᱫᱤ ᱟᱢ ᱵᱷᱤᱰᱤᱭᱳ ᱯᱞᱮᱭᱚᱯ ᱨᱮ ᱵᱞᱮᱠ ᱥᱠᱨᱤᱱ ᱟᱨᱵᱟᱝ ᱠᱷᱟᱹᱞᱤ ᱥᱴᱮᱴᱞᱤᱝ ᱮᱢ ᱧᱟᱢᱟ ᱮᱱᱠᱷᱟᱱ ᱢᱤᱰᱤᱭᱟ ᱴᱩᱱᱮᱞᱤᱝ ᱵᱚᱫᱚᱞ ᱢᱮ ᱾ - ᱪᱤᱛᱟᱹᱨ ᱪᱤᱱᱦᱟᱹ ᱠᱚ ᱧᱮᱞ ᱢᱮ ᱱᱟᱣᱟ ᱥᱴᱨᱤᱢ ᱞᱟᱹᱜᱤᱫ ᱪᱟᱪᱞᱟᱣ ᱢᱮ ᱢᱤᱫ error notification ᱛᱮᱭᱟᱨ ᱢᱮ ᱥᱮᱞᱮᱫ ᱮᱠᱥᱯᱳᱨᱴ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index f47e8e569..5f5887f5f 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -628,8 +628,6 @@ Sos cummentos sunt disabilitados Su creadore b\'at postu unu coro Marca comente pompiadu - Ammustra sos listrones colorados de Picasso in subra de sas immàgines chi indicant sa fonte issoro: ruja pro sa retze, biaita pro su discu e birde pro sa memòria - Ammustra sos indicadores de immàgines Impòsitos de chirca remota Impòsitos de chirca locales diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index ff6cd720a..27f34c9db 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -648,8 +648,6 @@ Nízka kvalita (menšie) Vysoká kvalita (väčšie) Náhľad miniatúry pri vyhľadávaní - Zobrazí farebné pásiky Picasso na obrázkoch podľa ich zdroja: červený pre sieť, modrý pre disk a zelený pre pamäť - Zobraziť indikátory obrázka Potiahnutím vymazať Komentáre sú zakázané Ak je automatické otáčanie zablokované, nespustí videá v miniprehrávači, ale prepne sa do celoobrazovkového režimu. Do miniprehrávača sa dostanete po ukončení režimu celej obrazovky diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 22d5fe16f..dc2a89c9c 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -627,8 +627,6 @@ Fallooyinka waa laxidhay Kahelay soosaaraha Waan daawaday - Soo bandhig shaambado midabka Picasso leh sawirrada dushooda oo tilmaamaya isha laga keenay: guduud waa khadka, buluug waa kaydka gudaha, cagaar waa kaydka K/G - Tus tilmaamayaasha sawirka Soojeedinada raadinta banaanka Soojeedinada raadinta gudaha Cabirka soodaarida udhexeeya diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 2d14361b3..33126a211 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -638,7 +638,6 @@ Означи као одгледано Коментари су онемогућени Обрађивање… Може потрајати пар тренутака - Прикажи индикаторе слике Не покрећите видео снимке у мини-плејеру, већ директно пређите на режим целог екрана, ако је аутоматска ротација закључана. И даље можете приступити мини-плејеру тако што ћете изаћи из целог екрана Покрени главни плејер преко целог екрана Срушите плејер @@ -734,7 +733,6 @@ Обавештења за пријаву грешака Увезите или извезите праћења из менија са 3 тачке Аудио снимак - Прикажите Picasso обојене траке на врху слика које указују на њихов извор: црвена за мрежу, плава за диск и зелена за меморију Направите обавештење о грешци Проценат Користите најновију верзију NewPipe-а diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index d9742c80e..1e58d2184 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -627,7 +627,6 @@ Du kan välja det natt-tema du föredrar nedan Välj det natt-tema du föredrar — %s Sökradens förhandsvisningsminiatyr - Visa bildindikatorer Lokala sökningsförslag Tog bort %1$s nedladdning @@ -645,7 +644,6 @@ Svep objekt för att ta bort dem Förslag via fjärrsökning Starta inte videor i minispelaren, utan byt till helskärmsläge direkt, om automatisk rotation är låst. Du kan fortfarande komma åt minispelaren genom att gå ut ur helskärmsläge - Visa Picasso färgade band ovanpå bilderna som anger deras källa: rött för nätverk, blått för disk och grönt för minne Sök efter uppdateringar Kolla manuellt efter nya versioner Söker efter uppdateringar… diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 068a099d2..86dd3cc43 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -641,8 +641,6 @@ மீடியா சுரங்கப்பாதையை முடக்கு நீங்கள் ஒரு கருப்பு திரை அனுபவித்தால் அல்லது வீடியோ பிளேபேக்கில் திணறினால் மீடியா சுரங்கப்பாதையை முடக்கு. உங்கள் சாதனத்தில் முன்னிருப்பாக மீடியா சுரங்கப்பாதை முடக்கப்பட்டது, ஏனெனில் உங்கள் சாதன மாதிரி அதை ஆதரிக்காது என்று அறியப்படுகிறது. - பட குறிகாட்டிகளைக் காட்டு - அவற்றின் மூலத்தைக் குறிக்கும் படங்களின் மேல் பிக்காசோ வண்ண ரிப்பன்களைக் காட்டு: நெட்வொர்க்கிற்கு சிவப்பு, வட்டுக்கு நீலம் மற்றும் நினைவகத்திற்கு பச்சை \"வீரரை செயலிழக்க\" காட்டு பிளேயரைப் பயன்படுத்தும் போது செயலிழப்பு விருப்பத்தைக் காட்டுகிறது புதிய நீரோடைகளுக்கு காசோலை இயக்கவும் diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index b95e04c0c..a422dc996 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -438,7 +438,6 @@ ప్లేబ్యాక్ స్పీడ్ నియంత్రణలు ఏమిలేదు మీరు బ్లాక్ స్క్రీన్ లేదా చలనచిత్రం ప్లేబ్యాక్‌లో అంతరాయాన్ని అనుభవిస్తే మీడియా టన్నెలింగ్‌ను నిలిపివేయండి - చిత్రాల మూలాన్ని సూచించే విధంగా వాటి పైభాగంలో పికాసో రంగు రిబ్బన్‌లను చూపండి: నెట్‌వర్క్ కోసం ఎరుపు, డిస్క్ కోసం నీలం మరియు మెమరీ కోసం ఆకుపచ్చ లోపం స్నాక్‌బార్‌ని చూపండి మీరు NewPipe యొక్క తాజా సంస్కరణను అమలు చేస్తున్నారు NewPipe నవీకరణ అందుబాటులో ఉంది! @@ -451,7 +450,6 @@ తక్కువ నాణ్యత (చిన్నది) చూపించవద్దు మీడియా టన్నెలింగ్‌ని నిలిపివేయండి - చిత్ర సూచికలను చూపు కొత్త స్ట్రీమ్‌ల కోసం తనిఖీని అమలు చేయండి ఎర్రర్ నోటిఫికేషన్‌ను సృష్టించండి దిగుమతి diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 9c84ee81d..76215fa0d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -627,8 +627,6 @@ Yorumlar devre dışı Yaratıcısınca kalplendi İzlendi olarak işaretle - Resimlerin üzerinde kaynaklarını gösteren Picasso renkli şeritler göster: ağ için kırmızı, disk için mavi ve bellek için yeşil - Resim göstergelerini göster Uzak arama önerileri Yerel arama önerileri diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index df8ee490d..c6dcdbdc1 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -640,8 +640,6 @@ Мініатюра з попереднім переглядом на повзунку поступу Вподобано автором Позначити переглянутим - Показувати кольорові стрічки Пікассо поверх зображень із зазначенням їх джерела: червоний для мережі, синій для диска та зелений для пам’яті - Показати індикатори зображень Віддалені пропозиції пошуку Локальні пошукові пропозиції diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index efa12f11d..e43b2f5df 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -618,8 +618,6 @@ Bình luận đã bị tắt Đã được chủ kênh thả \"thính\" Đánh dấu là đã xem - Hiển thị các dải băng màu Picasso trên đầu các hình ảnh cho biết nguồn của chúng: màu đỏ cho mạng, màu lam cho đĩa và màu lục cho bộ nhớ - Hiện dấu chỉ hình ảnh Đề xuất tìm kiếm trên mạng Đề xuất tìm kiếm cục bộ diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index adbf2ce64..ea92db43f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -618,8 +618,6 @@ 高品质(较大) 被创作者喜爱 标记为已观看 - 在图像顶部显示毕加索彩带,指示其来源:红色代表网络,蓝色代表磁盘,绿色代表内存 - 显示图像指示器 远程搜索建议 本地搜索建议 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 0dd10f1bc..7b2ba5bb1 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -662,9 +662,7 @@ 你係咪要刪除呢個谷? 淨係顯示未成谷嘅訂閱 - 啲圖都要騷 Picasso 三色碼顯示源頭:紅碼係網絡上高落嚟,藍碼係儲存喺磁碟本地,綠碼係潛伏喺記憶體中 服務原本嘅字會騷返喺串流項目上面 - 影像要推三色碼 若果播片嘅時候窒下窒下或者黑畫面,就停用多媒體隧道啦。 點樣用 Google 匯出嚟匯入 YouTube 訂閱: \n diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 1f4bfb290..cca70f771 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -600,8 +600,6 @@ 拖動列縮圖預覽 被創作者加心號 標記為已觀看 - 在圖片頂部顯示畢卡索彩色絲帶,指示其來源:紅色代表網路、藍色代表磁碟、綠色代表記憶體 - 顯示圖片指示器 遠端搜尋建議 本機搜尋建議 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9095fe927..7df964aed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -494,8 +494,6 @@ Disable media tunneling 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\" Shows a crash option when using the player Run check for new streams