From db7a90fad0fe6a52b0ac888ea9e1e22a5b535bbc Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Wed, 19 Jun 2024 10:19:02 +0530 Subject: [PATCH 01/13] Rename .java to .kt --- .../org/schabi/newpipe/database/{BasicDAO.java => BasicDAO.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/org/schabi/newpipe/database/{BasicDAO.java => BasicDAO.kt} (100%) diff --git a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.java b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt similarity index 100% rename from app/src/main/java/org/schabi/newpipe/database/BasicDAO.java rename to app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt From b273c4772e0bee167fc2b98abb469411a4ce157d Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Wed, 19 Jun 2024 10:19:02 +0530 Subject: [PATCH 02/13] Load notification icons using Coil --- app/build.gradle | 2 + .../org/schabi/newpipe/database/BasicDAO.kt | 34 ++-- .../newpipe/database/feed/dao/FeedDAO.kt | 37 ++-- .../newpipe/database/stream/dao/StreamDAO.kt | 31 ++- .../database/subscription/SubscriptionDAO.kt | 30 +-- .../newpipe/local/feed/FeedDatabaseManager.kt | 23 +-- .../feed/notifications/NotificationHelper.kt | 54 ++--- .../feed/notifications/NotificationWorker.kt | 52 ++--- .../local/feed/service/FeedLoadManager.kt | 186 +++++++++--------- .../local/subscription/SubscriptionManager.kt | 11 +- 10 files changed, 213 insertions(+), 247 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5502025a7..49b8c2248 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -228,6 +228,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation "androidx.room:room-runtime:${androidxRoomVersion}" implementation "androidx.room:room-rxjava3:${androidxRoomVersion}" + implementation "androidx.room:room-ktx:${androidxRoomVersion}" kapt "androidx.room:room-compiler:${androidxRoomVersion}" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' // Newer version specified to prevent accessibility regressions with RecyclerView, see: @@ -269,6 +270,7 @@ dependencies { // Image loading //noinspection GradleDependency --> 2.8 is the last version, not 2.71828! implementation "com.squareup.picasso:picasso:2.8" + implementation 'io.coil-kt:coil:2.6.0' // Markdown library for Android implementation "io.noties.markwon:core:${markwonVersion}" diff --git a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt index 255f5ba8d..fbd8aabef 100644 --- a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt @@ -1,39 +1,35 @@ -package org.schabi.newpipe.database; +package org.schabi.newpipe.database -import androidx.room.Dao; -import androidx.room.Delete; -import androidx.room.Insert; -import androidx.room.Update; - -import java.util.Collection; -import java.util.List; - -import io.reactivex.rxjava3.core.Flowable; +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Update +import io.reactivex.rxjava3.core.Flowable @Dao -public interface BasicDAO { +interface BasicDAO { /* Inserts */ @Insert - long insert(Entity entity); + fun insert(entity: Entity): Long @Insert - List insertAll(Collection entities); + fun insertAll(entities: Collection): List /* Searches */ - Flowable> getAll(); + fun getAll(): Flowable> - Flowable> listByService(int serviceId); + fun listByService(serviceId: Int): Flowable> /* Deletes */ @Delete - void delete(Entity entity); + fun delete(entity: Entity) - int deleteAll(); + fun deleteAll(): Int /* Updates */ @Update - int update(Entity entity); + suspend fun update(entity: Entity): Int @Update - void update(Collection entities); + suspend fun update(entities: Collection) } diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index e7ed93497..32665ebcb 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -8,6 +8,7 @@ import androidx.room.Transaction import androidx.room.Update import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe +import kotlinx.coroutines.flow.Flow import org.schabi.newpipe.database.feed.model.FeedEntity import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity @@ -18,9 +19,9 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity import java.time.OffsetDateTime @Dao -abstract class FeedDAO { +interface FeedDAO { @Query("DELETE FROM feed") - abstract fun deleteAll(): Int + suspend fun deleteAll(): Int /** * @param groupId the group id to get feed streams of; use @@ -86,7 +87,7 @@ abstract class FeedDAO { LIMIT 500 """ ) - abstract fun getStreams( + fun getStreams( groupId: Long, includePlayed: Boolean, includePartiallyPlayed: Boolean, @@ -119,7 +120,7 @@ abstract class FeedDAO { AND s.upload_date <> max_upload_date)) """ ) - abstract fun unlinkStreamsOlderThan(offsetDateTime: OffsetDateTime) + suspend fun unlinkStreamsOlderThan(offsetDateTime: OffsetDateTime) @Query( """ @@ -137,22 +138,22 @@ abstract class FeedDAO { ) """ ) - abstract fun unlinkOldLivestreams(subscriptionId: Long) + suspend fun unlinkOldLivestreams(subscriptionId: Long) @Insert(onConflict = OnConflictStrategy.IGNORE) - abstract fun insert(feedEntity: FeedEntity) + fun insert(feedEntity: FeedEntity) @Insert(onConflict = OnConflictStrategy.IGNORE) - abstract fun insertAll(entities: List): List + suspend fun insertAll(entities: List): List @Insert(onConflict = OnConflictStrategy.IGNORE) - internal abstract fun insertLastUpdated(lastUpdatedEntity: FeedLastUpdatedEntity): Long + suspend fun insertLastUpdated(lastUpdatedEntity: FeedLastUpdatedEntity): Long @Update(onConflict = OnConflictStrategy.IGNORE) - internal abstract fun updateLastUpdated(lastUpdatedEntity: FeedLastUpdatedEntity) + suspend fun updateLastUpdated(lastUpdatedEntity: FeedLastUpdatedEntity) @Transaction - open fun setLastUpdatedForSubscription(lastUpdatedEntity: FeedLastUpdatedEntity) { + suspend fun setLastUpdatedForSubscription(lastUpdatedEntity: FeedLastUpdatedEntity) { val id = insertLastUpdated(lastUpdatedEntity) if (id == -1L) { @@ -168,13 +169,13 @@ abstract class FeedDAO { ON fgs.subscription_id = lu.subscription_id AND fgs.group_id = :groupId """ ) - abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable> + fun oldestSubscriptionUpdate(groupId: Long): Flowable> @Query("SELECT MIN(last_updated) FROM feed_last_updated") - abstract fun oldestSubscriptionUpdateFromAll(): Flowable> + fun oldestSubscriptionUpdateFromAll(): Flowable> @Query("SELECT COUNT(*) FROM feed_last_updated WHERE last_updated IS NULL") - abstract fun notLoadedCount(): Flowable + fun notLoadedCount(): Flowable @Query( """ @@ -189,7 +190,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL """ ) - abstract fun notLoadedCountForGroup(groupId: Long): Flowable + fun notLoadedCountForGroup(groupId: Long): Flowable @Query( """ @@ -201,7 +202,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """ ) - abstract fun getAllOutdated(outdatedThreshold: OffsetDateTime): Flowable> + fun getAllOutdated(outdatedThreshold: OffsetDateTime): Flow> @Query( """ @@ -216,7 +217,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """ ) - abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: OffsetDateTime): Flowable> + fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: OffsetDateTime): Flow> @Query( """ @@ -230,8 +231,8 @@ abstract class FeedDAO { AND s.notification_mode = :notificationMode """ ) - abstract fun getOutdatedWithNotificationMode( + fun getOutdatedWithNotificationMode( outdatedThreshold: OffsetDateTime, @NotificationMode notificationMode: Int - ): Flowable> + ): Flow> } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt index d8c19c1e9..5d023507a 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt @@ -16,30 +16,30 @@ import org.schabi.newpipe.util.StreamTypeUtil import java.time.OffsetDateTime @Dao -abstract class StreamDAO : BasicDAO { +interface StreamDAO : BasicDAO { @Query("SELECT * FROM streams") - abstract override fun getAll(): Flowable> + override fun getAll(): Flowable> @Query("DELETE FROM streams") - abstract override fun deleteAll(): Int + override fun deleteAll(): Int @Query("SELECT * FROM streams WHERE service_id = :serviceId") - abstract override fun listByService(serviceId: Int): Flowable> + override fun listByService(serviceId: Int): Flowable> @Query("SELECT * FROM streams WHERE url = :url AND service_id = :serviceId") - abstract fun getStream(serviceId: Long, url: String): Flowable> + fun getStream(serviceId: Long, url: String): Flowable> @Query("UPDATE streams SET uploader_url = :uploaderUrl WHERE url = :url AND service_id = :serviceId") - abstract fun setUploaderUrl(serviceId: Long, url: String, uploaderUrl: String): Completable + fun setUploaderUrl(serviceId: Long, url: String, uploaderUrl: String): Completable @Insert(onConflict = OnConflictStrategy.IGNORE) - internal abstract fun silentInsertInternal(stream: StreamEntity): Long + suspend fun silentInsertInternal(stream: StreamEntity): Long @Insert(onConflict = OnConflictStrategy.IGNORE) - internal abstract fun silentInsertAllInternal(streams: List): List + suspend fun silentInsertAllInternal(streams: List): List @Query("SELECT COUNT(*) != 0 FROM streams WHERE url = :url AND service_id = :serviceId") - internal abstract fun exists(serviceId: Int, url: String): Boolean + suspend fun exists(serviceId: Int, url: String): Boolean @Query( """ @@ -47,10 +47,10 @@ abstract class StreamDAO : BasicDAO { FROM streams WHERE url = :url AND service_id = :serviceId """ ) - internal abstract fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed? + suspend fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed? @Transaction - open fun upsert(newerStream: StreamEntity): Long { + suspend fun upsert(newerStream: StreamEntity): Long { val uid = silentInsertInternal(newerStream) if (uid != -1L) { @@ -65,7 +65,7 @@ abstract class StreamDAO : BasicDAO { } @Transaction - open fun upsertAll(streams: List): List { + suspend fun upsertAll(streams: List): List { val insertUidList = silentInsertAllInternal(streams) val streamIds = ArrayList(streams.size) @@ -85,13 +85,12 @@ abstract class StreamDAO : BasicDAO { return streamIds } - private fun compareAndUpdateStream(newerStream: StreamEntity) { + private suspend fun compareAndUpdateStream(newerStream: StreamEntity) { val existentMinimalStream = getMinimalStreamForCompare(newerStream.serviceId, newerStream.url) ?: throw IllegalStateException("Stream cannot be null just after insertion.") newerStream.uid = existentMinimalStream.uid if (!StreamTypeUtil.isLiveStream(newerStream.streamType)) { - // Use the existent upload date if the newer stream does not have a better precision // (i.e. is an approximation). This is done to prevent unnecessary changes. val hasBetterPrecision = @@ -122,12 +121,12 @@ abstract class StreamDAO : BasicDAO { WHERE f.stream_id = streams.uid) """ ) - abstract fun deleteOrphans(): Int + suspend fun deleteOrphans(): Int /** * Minimal entry class used when comparing/updating an existent stream. */ - internal data class StreamCompareFeed( + data class StreamCompareFeed( @ColumnInfo(name = STREAM_ID) var uid: Long = 0, diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt index 47b6f4dd9..10f2dfc22 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt @@ -11,15 +11,15 @@ import io.reactivex.rxjava3.core.Maybe import org.schabi.newpipe.database.BasicDAO @Dao -abstract class SubscriptionDAO : BasicDAO { +interface SubscriptionDAO : BasicDAO { @Query("SELECT COUNT(*) FROM subscriptions") - abstract fun rowCount(): Flowable + fun rowCount(): Flowable @Query("SELECT * FROM subscriptions WHERE service_id = :serviceId") - abstract override fun listByService(serviceId: Int): Flowable> + override fun listByService(serviceId: Int): Flowable> @Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC") - abstract override fun getAll(): Flowable> + override fun getAll(): Flowable> @Query( """ @@ -30,7 +30,7 @@ abstract class SubscriptionDAO : BasicDAO { ORDER BY name COLLATE NOCASE ASC """ ) - abstract fun getSubscriptionsFiltered(filter: String): Flowable> + fun getSubscriptionsFiltered(filter: String): Flowable> @RewriteQueriesToDropUnusedColumns @Query( @@ -45,7 +45,7 @@ abstract class SubscriptionDAO : BasicDAO { ORDER BY name COLLATE NOCASE ASC """ ) - abstract fun getSubscriptionsOnlyUngrouped( + fun getSubscriptionsOnlyUngrouped( currentGroupId: Long ): Flowable> @@ -63,34 +63,34 @@ abstract class SubscriptionDAO : BasicDAO { ORDER BY name COLLATE NOCASE ASC """ ) - abstract fun getSubscriptionsOnlyUngroupedFiltered( + fun getSubscriptionsOnlyUngroupedFiltered( currentGroupId: Long, filter: String ): Flowable> @Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId") - abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable> + fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable> @Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId") - abstract fun getSubscription(serviceId: Int, url: String): Maybe + fun getSubscription(serviceId: Int, url: String): Maybe @Query("SELECT * FROM subscriptions WHERE uid = :subscriptionId") - abstract fun getSubscription(subscriptionId: Long): SubscriptionEntity + suspend fun getSubscription(subscriptionId: Long): SubscriptionEntity @Query("DELETE FROM subscriptions") - abstract override fun deleteAll(): Int + override fun deleteAll(): Int @Query("DELETE FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId") - abstract fun deleteSubscription(serviceId: Int, url: String): Int + fun deleteSubscription(serviceId: Int, url: String): Int @Query("SELECT uid FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId") - internal abstract fun getSubscriptionIdInternal(serviceId: Int, url: String): Long? + fun getSubscriptionIdInternal(serviceId: Int, url: String): Long? @Insert(onConflict = OnConflictStrategy.IGNORE) - internal abstract fun silentInsertAllInternal(entities: List): List + fun silentInsertAllInternal(entities: List): List @Transaction - open fun upsertAll(entities: List): List { + suspend fun upsertAll(entities: List): List { val insertUidList = silentInsertAllInternal(entities) insertUidList.forEachIndexed { index: Int, uidFromInsert: Long -> diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index ed65d4048..095511443 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -73,27 +73,22 @@ class FeedDatabaseManager(context: Context) { outdatedThreshold: OffsetDateTime ) = feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold) - fun markAsOutdated(subscriptionId: Long) = feedTable + suspend fun markAsOutdated(subscriptionId: Long) = feedTable .setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null)) - fun doesStreamExist(stream: StreamInfoItem): Boolean { + suspend fun doesStreamExist(stream: StreamInfoItem): Boolean { return streamTable.exists(stream.serviceId, stream.url) } - fun upsertAll( + suspend fun upsertAll( subscriptionId: Long, items: List, oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE ) { - val itemsToInsert = ArrayList() - loop@ for (streamItem in items) { - val uploadDate = streamItem.uploadDate - - itemsToInsert += when { - uploadDate == null && streamItem.streamType == StreamType.LIVE_STREAM -> streamItem - uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate -> streamItem - else -> continue@loop - } + val itemsToInsert = items.filter { + val uploadDate = it.uploadDate + uploadDate == null && it.streamType == StreamType.LIVE_STREAM || + uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate } feedTable.unlinkOldLivestreams(subscriptionId) @@ -111,12 +106,12 @@ class FeedDatabaseManager(context: Context) { ) } - fun removeOrphansOrOlderStreams(oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE) { + suspend fun removeOrphansOrOlderStreams(oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE) { feedTable.unlinkStreamsOlderThan(oldestAllowedDate) streamTable.deleteOrphans() } - fun clear() { + suspend fun clear() { feedTable.deleteAll() val deletedOrphans = streamTable.deleteOrphans() if (DEBUG) { 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 8ea89368d..3cc13dbfc 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 @@ -15,21 +14,21 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.content.ContextCompat import androidx.core.content.getSystemService +import androidx.core.graphics.drawable.toBitmapOrNull import androidx.preference.PreferenceManager -import com.squareup.picasso.Picasso -import com.squareup.picasso.Target +import coil.imageLoader +import coil.request.ImageRequest 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.ImageStrategy /** * 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 @@ -38,7 +37,7 @@ class NotificationHelper(val context: Context) { * Opening the summary notification will open the corresponding channel page. Opening the * individual notifications will open the corresponding video. */ - fun displayNewStreamsNotifications(data: FeedUpdateInfo) { + suspend fun displayNewStreamsNotifications(data: FeedUpdateInfo) { val newStreams = data.newStreams val summary = context.resources.getQuantityString( R.plurals.new_streams, newStreams.size, newStreams.size @@ -80,39 +79,20 @@ class NotificationHelper(val context: Context) { ) ) - // 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 request = ImageRequest.Builder(context) + .data(data.avatarUrl?.takeIf { ImageStrategy.shouldLoadImages() }) + .placeholder(R.drawable.ic_newpipe_triangle_white) + .error(R.drawable.ic_newpipe_triangle_white) + .build() + val avatarIcon = context.imageLoader.execute(request).drawable?.toBitmapOrNull() - // Show individual stream notifications, set channel icon only if there is actually - // one - showStreamNotifications(newStreams, data.serviceId, bitmap) - // Show summary notification - manager.notify(data.pseudoId, summaryBuilder.build()) + summaryBuilder.setLargeIcon(avatarIcon) - iconLoadingTargets.remove(this) // allow it to be garbage-collected - } - - override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) { - // Show individual stream notifications - showStreamNotifications(newStreams, data.serviceId, null) - // Show summary notification - manager.notify(data.pseudoId, summaryBuilder.build()) - iconLoadingTargets.remove(this) // allow it to be garbage-collected - } - - override fun onPrepareLoad(placeHolderDrawable: Drawable) { - // Nothing to do - } - } - - // 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) + // Show individual stream notifications, set channel icon only if there is actually + // one + showStreamNotifications(newStreams, data.serviceId, avatarIcon) + // Show summary notification + manager.notify(data.pseudoId, summaryBuilder.build()) } private fun showStreamNotifications( diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt index a40bf35dc..7db9f4e30 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt @@ -4,6 +4,7 @@ import android.content.Context import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.Constraints +import androidx.work.CoroutineWorker import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ForegroundInfo import androidx.work.NetworkType @@ -11,9 +12,6 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequest import androidx.work.WorkManager import androidx.work.WorkerParameters -import androidx.work.rxjava3.RxWorker -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Single import org.schabi.newpipe.App import org.schabi.newpipe.R import org.schabi.newpipe.error.ErrorInfo @@ -30,46 +28,38 @@ import java.util.concurrent.TimeUnit class NotificationWorker( appContext: Context, workerParams: WorkerParameters, -) : RxWorker(appContext, workerParams) { +) : CoroutineWorker(appContext, workerParams) { private val notificationHelper by lazy { NotificationHelper(appContext) } private val feedLoadManager = FeedLoadManager(appContext) - override fun createWork(): Single = if (areNotificationsEnabled(applicationContext)) { - feedLoadManager.startLoading( - ignoreOutdatedThreshold = true, - groupId = FeedLoadManager.GROUP_NOTIFICATION_ENABLED - ) - .doOnSubscribe { showLoadingFeedForegroundNotification() } - .map { feed -> - // filter out feedUpdateInfo items (i.e. channels) with nothing new - feed.mapNotNull { - it.value?.takeIf { feedUpdateInfo -> - feedUpdateInfo.newStreams.isNotEmpty() - } - } - } - .observeOn(AndroidSchedulers.mainThread()) // Picasso requires calls from main thread - .map { feedUpdateInfoList -> + override suspend fun doWork(): Result { + return if (areNotificationsEnabled(applicationContext)) { + try { + showLoadingFeedForegroundNotification() + val feedUpdateInfoList = feedLoadManager + .startLoading(FeedLoadManager.GROUP_NOTIFICATION_ENABLED, true) + // filter out feedUpdateInfo items (i.e. channels) with nothing new + .mapNotNull { it.value?.takeIf { it.newStreams.isNotEmpty() } } + // display notifications for each feedUpdateInfo (i.e. channel) - feedUpdateInfoList.forEach { feedUpdateInfo -> - notificationHelper.displayNewStreamsNotifications(feedUpdateInfo) + feedUpdateInfoList.forEach { + notificationHelper.displayNewStreamsNotifications(it) } - return@map Result.success() - } - .doOnError { throwable -> - Log.e(TAG, "Error while displaying streams notifications", throwable) + Result.success() + } catch (e: Exception) { + Log.e(TAG, "Error while displaying streams notifications", e) ErrorUtil.createNotification( applicationContext, - ErrorInfo(throwable, UserAction.NEW_STREAMS_NOTIFICATIONS, "main worker") + ErrorInfo(e, UserAction.NEW_STREAMS_NOTIFICATIONS, "main worker") ) + Result.failure() } - .onErrorReturnItem(Result.failure()) - } else { - // the user can disable streams notifications in the device's app settings - Single.just(Result.success()) + } else { + Result.success() + } } private fun showLoadingFeedForegroundNotification() { diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt index 0b6a8068c..91decf904 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt @@ -3,6 +3,7 @@ package org.schabi.newpipe.local.feed.service import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceManager +import androidx.room.withTransaction import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Flowable @@ -11,6 +12,21 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.functions.Consumer import io.reactivex.rxjava3.processors.PublishProcessor import io.reactivex.rxjava3.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMap +import kotlinx.coroutines.flow.flatMapConcat +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.observeOn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.withContext +import okhttp3.Dispatcher import org.schabi.newpipe.R import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.subscription.NotificationMode @@ -56,10 +72,10 @@ class FeedLoadManager(private val context: Context) { * within the `feed_update_threshold` are checked for updates. This threshold can be set by * the user in the app settings. When `true`, all subscriptions are checked for new streams. */ - fun startLoading( + suspend fun startLoading( groupId: Long = FeedGroupEntity.GROUP_ALL_ID, ignoreOutdatedThreshold: Boolean = false, - ): Single>> { + ): List> { val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) val useFeedExtractor = defaultSharedPreferences.getBoolean( context.getString(R.string.feed_use_dedicated_fetch_method_key), @@ -91,36 +107,72 @@ class FeedLoadManager(private val context: Context) { else -> feedDatabaseManager.outdatedSubscriptionsForGroup(groupId, outdatedThreshold) } - return outdatedSubscriptions - .take(1) - .doOnNext { - currentProgress.set(0) - maxProgress.set(it.size) + val notifications = withContext(Dispatchers.IO) { + outdatedSubscriptions + .take(1) + .onEach { + currentProgress.set(0) + maxProgress.set(it.size) + } + .filter { it.isNotEmpty() } + .onEach { + notificationUpdater.onNext("") + broadcastProgress() + } + .takeWhile { !cancelSignal.get() } + .filter { !cancelSignal.get() } + .flatMapConcat { it -> + it.map { loadStreams(it, useFeedExtractor, defaultSharedPreferences) } + .asFlow() + } + .onEach { + currentProgress.incrementAndGet() + notificationUpdater.onNext(it.value?.name.orEmpty()) + + broadcastProgress() + } + .buffer(BUFFER_COUNT_BEFORE_INSERT) + .toList() + } + + feedDatabaseManager.database().withTransaction { + for (notification in notifications) { + when { + notification.isOnNext -> { + val info = notification.value!! + + notification.value!!.newStreams = filterNewStreams(info.streams) + + feedDatabaseManager.upsertAll(info.uid, info.streams) + subscriptionManager.updateFromInfo(info) + + if (info.errors.isNotEmpty()) { + feedResultsHolder.addErrors( + info.errors.map { + FeedLoadService.RequestException( + info.uid, + "${info.serviceId}:${info.url}", + it + ) + } + ) + feedDatabaseManager.markAsOutdated(info.uid) + } + } + notification.isOnError -> { + val error = notification.error + feedResultsHolder.addError(error!!) + + if (error is FeedLoadService.RequestException) { + feedDatabaseManager.markAsOutdated(error.subscriptionId) + } + } + } } - .filter { it.isNotEmpty() } - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { - notificationUpdater.onNext("") - broadcastProgress() - } - .observeOn(Schedulers.io()) - .flatMap { Flowable.fromIterable(it) } - .takeWhile { !cancelSignal.get() } - .parallel(PARALLEL_EXTRACTIONS, PARALLEL_EXTRACTIONS * 2) - .runOn(Schedulers.io(), PARALLEL_EXTRACTIONS * 2) - .filter { !cancelSignal.get() } - .map { subscriptionEntity -> - loadStreams(subscriptionEntity, useFeedExtractor, defaultSharedPreferences) - } - .sequential() - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext(NotificationConsumer()) - .observeOn(Schedulers.io()) - .buffer(BUFFER_COUNT_BEFORE_INSERT) - .doOnNext(DatabaseConsumer()) - .subscribeOn(Schedulers.io()) - .toList() - .flatMap { x -> postProcessFeed().toSingleDefault(x.flatten()) } + } + + postProcessFeed() + return notifications } fun cancel() { @@ -243,79 +295,29 @@ class FeedLoadManager(private val context: Context) { * Remove streams from the feed which are older than [FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE]. * Remove streams from the database which are not linked / used by any table. */ - private fun postProcessFeed() = Completable.fromRunnable { - FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(R.string.feed_processing_message)) - feedDatabaseManager.removeOrphansOrOlderStreams() + private suspend fun postProcessFeed() { + withContext(Dispatchers.IO) { + FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(R.string.feed_processing_message)) + feedDatabaseManager.removeOrphansOrOlderStreams() + + FeedEventManager.postEvent(FeedEventManager.Event.SuccessResultEvent(feedResultsHolder.itemsErrors)) + } - FeedEventManager.postEvent(FeedEventManager.Event.SuccessResultEvent(feedResultsHolder.itemsErrors)) - }.doOnSubscribe { currentProgress.set(-1) maxProgress.set(-1) notificationUpdater.onNext(context.getString(R.string.feed_processing_message)) FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(R.string.feed_processing_message)) - }.subscribeOn(Schedulers.io()) - - private inner class NotificationConsumer : Consumer> { - override fun accept(item: Notification) { - currentProgress.incrementAndGet() - notificationUpdater.onNext(item.value?.name.orEmpty()) - - broadcastProgress() - } } - private inner class DatabaseConsumer : Consumer>> { - - override fun accept(list: List>) { - feedDatabaseManager.database().runInTransaction { - for (notification in list) { - when { - notification.isOnNext -> { - val info = notification.value!! - - notification.value!!.newStreams = filterNewStreams(info.streams) - - feedDatabaseManager.upsertAll(info.uid, info.streams) - subscriptionManager.updateFromInfo(info) - - if (info.errors.isNotEmpty()) { - feedResultsHolder.addErrors( - info.errors.map { - FeedLoadService.RequestException( - info.uid, - "${info.serviceId}:${info.url}", - it - ) - } - ) - feedDatabaseManager.markAsOutdated(info.uid) - } - } - notification.isOnError -> { - val error = notification.error - feedResultsHolder.addError(error!!) - - if (error is FeedLoadService.RequestException) { - feedDatabaseManager.markAsOutdated(error.subscriptionId) - } - } - } - } - } - } - - private fun filterNewStreams(list: List): List { - return list.filter { - !feedDatabaseManager.doesStreamExist(it) && + private suspend fun filterNewStreams(list: List): List { + return list.filter { + !feedDatabaseManager.doesStreamExist(it) && it.uploadDate != null && // Streams older than this date are automatically removed from the feed. // Therefore, streams which are not in the database, // but older than this date, are considered old. - it.uploadDate!!.offsetDateTime().isAfter( - FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE - ) - } + it.uploadDate!!.offsetDateTime() > FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE } } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt index 474add4f4..757d60f91 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt @@ -2,6 +2,7 @@ package org.schabi.newpipe.local.subscription import android.content.Context import android.util.Pair +import androidx.room.withTransaction import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Flowable @@ -26,7 +27,7 @@ class SubscriptionManager(context: Context) { private val feedDatabaseManager = FeedDatabaseManager(context) fun subscriptionTable(): SubscriptionDAO = subscriptionTable - fun subscriptions() = subscriptionTable.all + fun subscriptions() = subscriptionTable.getAll() fun getSubscriptions( currentGroupId: Long = FeedGroupEntity.GROUP_ALL_ID, @@ -44,16 +45,16 @@ class SubscriptionManager(context: Context) { } } showOnlyUngrouped -> subscriptionTable.getSubscriptionsOnlyUngrouped(currentGroupId) - else -> subscriptionTable.all + else -> subscriptionTable.getAll() } } - fun upsertAll(infoList: List>>): List { + suspend fun upsertAll(infoList: List>>): List { val listEntities = subscriptionTable.upsertAll( infoList.map { SubscriptionEntity.from(it.first) } ) - database.runInTransaction { + database.withTransaction { infoList.forEachIndexed { index, info -> info.second.forEach { feedDatabaseManager.upsertAll( @@ -96,7 +97,7 @@ class SubscriptionManager(context: Context) { } } - fun updateFromInfo(info: FeedUpdateInfo) { + suspend fun updateFromInfo(info: FeedUpdateInfo) { val subscriptionEntity = subscriptionTable.getSubscription(info.uid) subscriptionEntity.name = info.name From 5b445fd877c9fb3506be9271ec972e06ee884083 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 06:02:51 +0530 Subject: [PATCH 03/13] Fix compilation errors --- app/build.gradle | 3 +- .../org/schabi/newpipe/database/BasicDAO.kt | 3 -- .../history/dao/SearchHistoryDAO.java | 4 ++ .../database/playlist/dao/PlaylistDAO.java | 4 ++ .../playlist/dao/PlaylistRemoteDAO.java | 25 +++-------- .../newpipe/database/stream/dao/StreamDAO.kt | 29 ++++++------- .../database/stream/dao/StreamStateDAO.java | 14 ++---- .../newpipe/local/feed/FeedDatabaseManager.kt | 2 +- .../schabi/newpipe/local/feed/FeedFragment.kt | 38 ++++++++-------- .../local/feed/service/FeedLoadManager.kt | 18 +++----- .../local/feed/service/FeedLoadService.kt | 43 +++++++++---------- .../local/history/HistoryRecordManager.java | 15 +++---- .../local/playlist/LocalPlaylistManager.java | 4 +- .../local/subscription/SubscriptionManager.kt | 13 +++--- .../schabi/newpipe/util/SparseItemUtil.java | 2 +- 15 files changed, 94 insertions(+), 123 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 49b8c2248..5275a9add 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,7 +113,7 @@ android { ext { checkstyleVersion = '10.12.1' - androidxLifecycleVersion = '2.6.2' + androidxLifecycleVersion = '2.8.2' androidxRoomVersion = '2.6.1' androidxWorkVersion = '2.8.1' @@ -222,6 +222,7 @@ dependencies { implementation 'androidx.fragment:fragment-ktx:1.6.2' implementation "androidx.lifecycle:lifecycle-livedata-ktx:${androidxLifecycleVersion}" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${androidxLifecycleVersion}" + implementation "androidx.lifecycle:lifecycle-service:${androidxLifecycleVersion}" implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' implementation 'androidx.media:media:1.7.0' implementation 'androidx.preference:preference:1.2.1' diff --git a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt index fbd8aabef..3f35d0ff9 100644 --- a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt @@ -29,7 +29,4 @@ interface BasicDAO { /* Updates */ @Update suspend fun update(entity: Entity): Int - - @Update - suspend fun update(entities: Collection) } diff --git a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java index 8a281bdb4..02d9fa555 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.database.history.dao; import androidx.annotation.Nullable; import androidx.room.Dao; import androidx.room.Query; +import androidx.room.Update; import org.schabi.newpipe.database.history.model.SearchHistoryEntry; @@ -21,6 +22,9 @@ public interface SearchHistoryDAO extends HistoryDAO { String ORDER_BY_CREATION_DATE = " ORDER BY " + CREATION_DATE + " DESC"; String ORDER_BY_MAX_CREATION_DATE = " ORDER BY MAX(" + CREATION_DATE + ") DESC"; + @Update + int update(SearchHistoryEntry historyEntry); + @Query("SELECT * FROM " + TABLE_NAME + " WHERE " + ID + " = (SELECT MAX(" + ID + ") FROM " + TABLE_NAME + ")") @Nullable diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java index d8071e0af..25401c160 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.database.playlist.dao; import androidx.room.Dao; import androidx.room.Query; import androidx.room.Transaction; +import androidx.room.Update; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistEntity; @@ -24,6 +25,9 @@ public interface PlaylistDAO extends BasicDAO { @Query("DELETE FROM " + PLAYLIST_TABLE) int deleteAll(); + @Update + int update(PlaylistEntity playlist); + @Override default Flowable> listByService(final int serviceId) { throw new UnsupportedOperationException(); diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java index 8ab8a2afd..cb9c0bbdd 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java @@ -2,7 +2,8 @@ package org.schabi.newpipe.database.playlist.dao; import androidx.room.Dao; import androidx.room.Query; -import androidx.room.Transaction; +import androidx.room.Update; +import androidx.room.Upsert; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; @@ -19,6 +20,9 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.RE @Dao public interface PlaylistRemoteDAO extends BasicDAO { + @Update + int update(PlaylistRemoteEntity entity); + @Override @Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE) Flowable> getAll(); @@ -44,23 +48,8 @@ public interface PlaylistRemoteDAO extends BasicDAO { + " ORDER BY " + REMOTE_PLAYLIST_DISPLAY_INDEX) Flowable> getPlaylists(); - @Query("SELECT " + REMOTE_PLAYLIST_ID + " FROM " + REMOTE_PLAYLIST_TABLE - + " WHERE " + REMOTE_PLAYLIST_URL + " = :url " - + "AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId") - Long getPlaylistIdInternal(long serviceId, String url); - - @Transaction - default long upsert(final PlaylistRemoteEntity playlist) { - final Long playlistId = getPlaylistIdInternal(playlist.getServiceId(), playlist.getUrl()); - - if (playlistId == null) { - return insert(playlist); - } else { - playlist.setUid(playlistId); - update(playlist); - return playlistId; - } - } + @Upsert + long upsert(PlaylistRemoteEntity playlist); @Query("DELETE FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " + REMOTE_PLAYLIST_ID + " = :playlistId") diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt index 5d023507a..c7f05a1aa 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt @@ -8,6 +8,7 @@ import androidx.room.Query import androidx.room.Transaction import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Flowable +import kotlinx.coroutines.runBlocking import org.schabi.newpipe.database.BasicDAO import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID @@ -64,25 +65,17 @@ interface StreamDAO : BasicDAO { return newerStream.uid } + fun upsertBlocking(newerStream: StreamEntity) = runBlocking { + upsert(newerStream) + } + @Transaction suspend fun upsertAll(streams: List): List { - val insertUidList = silentInsertAllInternal(streams) + return streams.map { upsert(it) } + } - val streamIds = ArrayList(streams.size) - for ((index, uid) in insertUidList.withIndex()) { - val newerStream = streams[index] - if (uid != -1L) { - streamIds.add(uid) - newerStream.uid = uid - continue - } - - compareAndUpdateStream(newerStream) - streamIds.add(newerStream.uid) - } - - update(streams) - return streamIds + fun upsertAllBlocking(streams: List) = runBlocking { + upsertAll(streams) } private suspend fun compareAndUpdateStream(newerStream: StreamEntity) { @@ -123,6 +116,10 @@ interface StreamDAO : BasicDAO { ) suspend fun deleteOrphans(): Int + fun deleteOrphansBlocking() = runBlocking { + deleteOrphans() + } + /** * Minimal entry class used when comparing/updating an existent stream. */ diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java index 06371248d..7bb08de2a 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java @@ -1,10 +1,8 @@ package org.schabi.newpipe.database.stream.dao; import androidx.room.Dao; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; import androidx.room.Query; -import androidx.room.Transaction; +import androidx.room.Upsert; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.stream.model.StreamStateEntity; @@ -37,12 +35,6 @@ public interface StreamStateDAO extends BasicDAO { @Query("DELETE FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId") int deleteState(long streamId); - @Insert(onConflict = OnConflictStrategy.IGNORE) - void silentInsertInternal(StreamStateEntity streamState); - - @Transaction - default long upsert(final StreamStateEntity stream) { - silentInsertInternal(stream); - return update(stream); - } + @Upsert + long upsert(StreamStateEntity stream); } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index 095511443..33a719526 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -88,7 +88,7 @@ class FeedDatabaseManager(context: Context) { val itemsToInsert = items.filter { val uploadDate = it.uploadDate uploadDate == null && it.streamType == StreamType.LIVE_STREAM || - uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate + uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate } feedTable.unlinkOldLivestreams(subscriptionId) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index e8c5b1e34..b716380ca 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -41,6 +41,7 @@ import androidx.core.content.edit import androidx.core.os.bundleOf import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -49,10 +50,9 @@ import com.xwray.groupie.Item import com.xwray.groupie.OnItemClickListener import com.xwray.groupie.OnItemLongClickListener import icepick.State -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable -import io.reactivex.rxjava3.schedulers.Schedulers +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.launch import org.schabi.newpipe.NewPipeDatabase import org.schabi.newpipe.R import org.schabi.newpipe.database.feed.model.FeedGroupEntity @@ -253,7 +253,7 @@ class FeedFragment : BaseStateFragment() { viewModel.getShowFutureItemsFromPreferences() ) - AlertDialog.Builder(context!!) + AlertDialog.Builder(requireContext()) .setTitle(R.string.feed_hide_streams_title) .setMultiChoiceItems(dialogItems, checkedDialogItems) { _, which, isChecked -> checkedDialogItems[which] = isChecked @@ -453,24 +453,20 @@ class FeedFragment : BaseStateFragment() { if (t is FeedLoadService.RequestException && t.cause is ContentNotAvailableException ) { - disposables.add( - Single.fromCallable { - NewPipeDatabase.getInstance(requireContext()).subscriptionDAO() - .getSubscription(t.subscriptionId) + lifecycleScope.launch( + CoroutineExceptionHandler { _, throwable -> + Log.e(TAG, "Unable to process", throwable) } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { subscriptionEntity -> - handleFeedNotAvailable( - subscriptionEntity, - t.cause, - errors.subList(i + 1, errors.size) - ) - }, - { throwable -> Log.e(TAG, "Unable to process", throwable) } - ) - ) + ) { + val subscriptionEntity = NewPipeDatabase.getInstance(requireContext()) + .subscriptionDAO() + .getSubscription(t.subscriptionId) + handleFeedNotAvailable( + subscriptionEntity, + t.cause, + errors.subList(i + 1, errors.size) + ) + } // this will be called on the remaining errors by handleFeedNotAvailable() return@handleItemsErrors } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt index 91decf904..59729ecf4 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt @@ -4,29 +4,21 @@ import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceManager import androidx.room.withTransaction -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Notification -import io.reactivex.rxjava3.core.Single -import io.reactivex.rxjava3.functions.Consumer import io.reactivex.rxjava3.processors.PublishProcessor -import io.reactivex.rxjava3.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMap import kotlinx.coroutines.flow.flatMapConcat -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.observeOn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withContext -import okhttp3.Dispatcher import org.schabi.newpipe.R import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.subscription.NotificationMode @@ -313,11 +305,11 @@ class FeedLoadManager(private val context: Context) { private suspend fun filterNewStreams(list: List): List { return list.filter { !feedDatabaseManager.doesStreamExist(it) && - it.uploadDate != null && - // Streams older than this date are automatically removed from the feed. - // Therefore, streams which are not in the database, - // but older than this date, are considered old. - it.uploadDate!!.offsetDateTime() > FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE + it.uploadDate != null && + // Streams older than this date are automatically removed from the feed. + // Therefore, streams which are not in the database, + // but older than this date, are considered old. + it.uploadDate!!.offsetDateTime() > FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt index f960040de..654179f6f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt @@ -19,22 +19,24 @@ package org.schabi.newpipe.local.feed.service -import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Build -import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.app.ServiceCompat +import androidx.lifecycle.LifecycleService +import androidx.lifecycle.lifecycleScope import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.functions.Function +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.launch import org.schabi.newpipe.App import org.schabi.newpipe.MainActivity.DEBUG import org.schabi.newpipe.R @@ -43,7 +45,7 @@ import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ErrorResultE import org.schabi.newpipe.local.feed.service.FeedEventManager.postEvent import java.util.concurrent.TimeUnit -class FeedLoadService : Service() { +class FeedLoadService : LifecycleService() { companion object { private val TAG = FeedLoadService::class.java.simpleName const val NOTIFICATION_ID = 7293450 @@ -72,35 +74,34 @@ class FeedLoadService : Service() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + super.onStartCommand(intent, flags, startId) + if (DEBUG) { Log.d( TAG, - "onStartCommand() called with: intent = [" + intent + "]," + - " flags = [" + flags + "], startId = [" + startId + "]" + "onStartCommand() called with: intent = [$intent], flags = [$flags], " + + "startId = [$startId]" ) } - if (intent == null || loadingDisposable != null) { + if (intent == null) { return START_NOT_STICKY } setupNotification() setupBroadcastReceiver() - val groupId = intent.getLongExtra(EXTRA_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID) - loadingDisposable = feedLoadManager.startLoading(groupId) - .observeOn(AndroidSchedulers.mainThread()) - .doOnSubscribe { - startForeground(NOTIFICATION_ID, notificationBuilder.build()) - } - .subscribe { _, error: Throwable? -> // explicitly mark error as nullable - if (error != null) { - Log.e(TAG, "Error while storing result", error) - handleError(error) - return@subscribe - } - stopService() + lifecycleScope.launch( + CoroutineExceptionHandler { _, throwable -> + Log.e(TAG, "Error while storing result", throwable) + handleError(throwable) } + ) { + val groupId = intent.getLongExtra(EXTRA_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID) + startForeground(NOTIFICATION_ID, notificationBuilder.build()) + feedLoadManager.startLoading(groupId) + stopService() + } return START_NOT_STICKY } @@ -116,10 +117,6 @@ class FeedLoadService : Service() { stopSelf() } - override fun onBind(intent: Intent): IBinder? { - return null - } - // ///////////////////////////////////////////////////////////////////////// // Loading & Handling // ///////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index ed3cf548f..1689d74d6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -110,10 +110,10 @@ public class HistoryRecordManager { .subscribeOn(Schedulers.io()) .blockingGet(); duration = completeInfo.getDuration(); - streamId = streamTable.upsert(new StreamEntity(completeInfo)); + streamId = streamTable.upsertBlocking(new StreamEntity(completeInfo)); } else { duration = info.getDuration(); - streamId = streamTable.upsert(new StreamEntity(info)); + streamId = streamTable.upsertBlocking(new StreamEntity(info)); } // Update the stream progress to the full duration of the video @@ -141,7 +141,7 @@ public class HistoryRecordManager { final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); return Maybe.fromCallable(() -> database.runInTransaction(() -> { - final long streamId = streamTable.upsert(new StreamEntity(info)); + final long streamId = streamTable.upsertBlocking(new StreamEntity(info)); final StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId); if (latestEntry != null) { @@ -236,7 +236,7 @@ public class HistoryRecordManager { public Maybe loadStreamState(final PlayQueueItem queueItem) { return queueItem.getStream() - .map(info -> streamTable.upsert(new StreamEntity(info))) + .map(info -> streamTable.upsertBlocking(new StreamEntity(info))) .flatMapPublisher(streamStateTable::getState) .firstElement() .flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0))) @@ -245,7 +245,7 @@ public class HistoryRecordManager { } public Maybe loadStreamState(final StreamInfo info) { - return Single.fromCallable(() -> streamTable.upsert(new StreamEntity(info))) + return Single.fromCallable(() -> streamTable.upsertBlocking(new StreamEntity(info))) .flatMapPublisher(streamStateTable::getState) .firstElement() .flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0))) @@ -255,7 +255,7 @@ public class HistoryRecordManager { public Completable saveStreamState(@NonNull final StreamInfo info, final long progressMillis) { return Completable.fromAction(() -> database.runInTransaction(() -> { - final long streamId = streamTable.upsert(new StreamEntity(info)); + final long streamId = streamTable.upsertBlocking(new StreamEntity(info)); final StreamStateEntity state = new StreamStateEntity(streamId, progressMillis); if (state.isValid(info.getDuration())) { streamStateTable.upsert(state); @@ -312,7 +312,6 @@ public class HistoryRecordManager { /////////////////////////////////////////////////////// public Single removeOrphanedRecords() { - return Single.fromCallable(streamTable::deleteOrphans).subscribeOn(Schedulers.io()); + return Single.fromCallable(streamTable::deleteOrphansBlocking).subscribeOn(Schedulers.io()); } - } diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java index dd9307675..2b6b9fee1 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java @@ -46,7 +46,7 @@ public class LocalPlaylistManager { // Make sure the new playlist is always on the top of bookmark. // The index will be reassigned to non-negative number in BookmarkFragment. return Maybe.fromCallable(() -> database.runInTransaction(() -> { - final List streamIds = streamTable.upsertAll(streams); + final List streamIds = streamTable.upsertAllBlocking(streams); final PlaylistEntity newPlaylist = new PlaylistEntity(name, false, streamIds.get(0), -1); @@ -61,7 +61,7 @@ public class LocalPlaylistManager { return playlistStreamTable.getMaximumIndexOf(playlistId) .firstElement() .map(maxJoinIndex -> database.runInTransaction(() -> { - final List streamIds = streamTable.upsertAll(streams); + final List streamIds = streamTable.upsertAllBlocking(streams); return insertJoinEntities(playlistId, streamIds, maxJoinIndex + 1); } )).subscribeOn(Schedulers.io()); diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt index 757d60f91..87d36746e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt @@ -7,6 +7,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.schedulers.Schedulers +import kotlinx.coroutines.runBlocking import org.schabi.newpipe.NewPipeDatabase import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.stream.model.StreamEntity @@ -49,7 +50,9 @@ class SubscriptionManager(context: Context) { } } - suspend fun upsertAll(infoList: List>>): List { + fun upsertAll( + infoList: List>> + ): List = runBlocking { val listEntities = subscriptionTable.upsertAll( infoList.map { SubscriptionEntity.from(it.first) } ) @@ -65,7 +68,7 @@ class SubscriptionManager(context: Context) { } } - return listEntities + listEntities } fun updateChannelInfo(info: ChannelInfo): Completable = @@ -78,7 +81,7 @@ class SubscriptionManager(context: Context) { info.description, info.subscriberCount ) - subscriptionTable.update(it) + runBlocking { subscriptionTable.update(it) } } } @@ -87,7 +90,7 @@ class SubscriptionManager(context: Context) { .flatMapCompletable { entity: SubscriptionEntity -> Completable.fromAction { entity.notificationMode = mode - subscriptionTable().update(entity) + runBlocking { subscriptionTable().update(entity) } }.apply { if (mode != NotificationMode.DISABLED) { // notifications have just been enabled, mark all streams as "old" @@ -139,7 +142,7 @@ class SubscriptionManager(context: Context) { .map { channel -> channel.relatedItems.filterIsInstance().map { stream -> StreamEntity(stream) } } .flatMapCompletable { entities -> Completable.fromAction { - database.streamDAO().upsertAll(entities) + runBlocking { database.streamDAO().upsertAll(entities) } } }.onErrorComplete() } diff --git a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java index 6e9ea7a47..4a17b27eb 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java +++ b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java @@ -108,7 +108,7 @@ public final class SparseItemUtil { .subscribe(result -> { // save to database in the background (not on main thread) Completable.fromAction(() -> NewPipeDatabase.getInstance(context) - .streamDAO().upsert(new StreamEntity(result))) + .streamDAO().upsertBlocking(new StreamEntity(result))) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .doOnError(throwable -> From 465fc9f2a8eaa39532c35d983e12216792cb1557 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 08:32:06 +0530 Subject: [PATCH 04/13] Migrate to Coil from Picasso --- app/src/main/java/org/schabi/newpipe/App.java | 32 +++++-- .../fragments/detail/VideoDetailFragment.java | 27 +++--- .../list/channel/ChannelFragment.java | 15 ++- .../list/comments/CommentRepliesFragment.java | 4 +- .../list/playlist/PlaylistFragment.java | 5 +- .../newpipe/info_list/StreamSegmentItem.kt | 44 ++++----- .../holder/ChannelMiniInfoItemHolder.java | 4 +- .../holder/CommentInfoItemHolder.java | 8 +- .../holder/PlaylistMiniInfoItemHolder.java | 4 +- .../holder/StreamMiniInfoItemHolder.java | 4 +- .../newpipe/local/feed/item/StreamItem.kt | 4 +- .../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 | 6 +- .../playqueue/PlayQueueItemBuilder.java | 4 +- .../SeekbarPreviewThumbnailHolder.java | 7 +- .../settings/ContentSettingsFragment.java | 20 ++-- .../settings/SelectChannelFragment.java | 4 +- .../settings/SelectPlaylistFragment.java | 17 ++-- .../schabi/newpipe/util/image/CoilHelper.kt | 94 +++++++++++++++++++ .../newpipe/util/image/PicassoHelper.java | 62 +----------- 25 files changed, 226 insertions(+), 173 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index d92425d20..22ee3aa8b 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -20,10 +20,10 @@ import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.ktx.ExceptionUtils; import org.schabi.newpipe.settings.NewPipeSettings; 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.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 java.io.IOException; @@ -32,6 +32,11 @@ import java.net.SocketException; import java.util.List; import java.util.Objects; +import coil.ImageLoader; +import coil.ImageLoaderFactory; +import coil.disk.DiskCache; +import coil.memory.MemoryCache; +import coil.util.DebugLogger; import io.reactivex.rxjava3.exceptions.CompositeException; import io.reactivex.rxjava3.exceptions.MissingBackpressureException; import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException; @@ -57,7 +62,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; * along with NewPipe. If not, see . */ -public class App extends Application { +public class App extends Application implements ImageLoaderFactory { public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; private static final String TAG = App.class.toString(); @@ -118,10 +123,25 @@ public class App extends Application { configureRxJavaErrorHandler(); } + @NonNull @Override - public void onTerminate() { - super.onTerminate(); - PicassoHelper.terminate(); + public ImageLoader newImageLoader() { + final var builder = new ImageLoader.Builder(this) + .memoryCache(() -> new MemoryCache.Builder(this) + .maxSizeBytes(10 * 1024 * 1024) + .build()) + .diskCache(() -> new DiskCache.Builder() + .maxSizeBytes(50 * 1024 * 1024) + .build()) + .allowRgb565(true); + + final var prefs = PreferenceManager.getDefaultSharedPreferences(this); + if (MainActivity.DEBUG + && prefs.getBoolean(getString(R.string.show_image_indicators_key), false)) { + builder.logger(new DebugLogger()); + } + + return builder.build(); } protected Downloader getDownloader() { 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 95b54f65a..da4b071c3 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 @@ -116,7 +116,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; @@ -127,6 +127,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import coil.util.CoilUtils; import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.CompositeDisposable; @@ -1471,7 +1472,10 @@ public final class VideoDetailFragment } } - PicassoHelper.cancelTag(PICASSO_VIDEO_DETAILS_TAG); + CoilUtils.dispose(binding.detailThumbnailImageView); + CoilUtils.dispose(binding.detailSubChannelThumbnailView); + CoilUtils.dispose(binding.overlayThumbnail); + binding.detailThumbnailImageView.setImageBitmap(null); binding.detailSubChannelThumbnailView.setImageBitmap(null); } @@ -1562,8 +1566,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); @@ -1613,8 +1617,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); } @@ -1645,11 +1649,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); } @@ -2403,8 +2407,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 7e83d9958..bab3cf907 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 @@ -49,10 +49,11 @@ import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; 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.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; @@ -580,17 +581,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 304eaf55a..4eb73520f 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 @@ -23,8 +23,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 java.util.Queue; @@ -82,7 +82,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 9afb06344..09c9682c7 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,6 +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.CoilHelper; import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.text.TextEllipsizer; @@ -327,8 +328,8 @@ public class PlaylistFragment extends BaseListInfoFragment() { +) : BindableItem() { companion object { const val PAYLOAD_SELECT = 1 @@ -21,31 +20,32 @@ 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 { onClick.onItemLongClick(this, item.startTimeSeconds); true } - viewHolder.root.isSelected = isSelected + viewBinding.root.setOnClickListener { onClick.onItemClick(this, item.startTimeSeconds) } + viewBinding.root.setOnLongClickListener { onClick.onItemLongClick(this, item.startTimeSeconds); true } + 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 @@ -54,4 +54,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 a3f0384ad..ac674945f 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 @@ -23,8 +23,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.CommentTextOnTouchListener; import org.schabi.newpipe.util.text.TextEllipsizer; @@ -79,14 +79,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/local/feed/item/StreamItem.kt b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt index 4a071d6df..030bb7a76 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 @@ -19,7 +19,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 import java.util.concurrent.TimeUnit import java.util.function.Consumer @@ -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/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 336f5cfe3..a11438374 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.name); itemStreamCountView.setText(Localization.localizeStreamCountMini( itemStreamCountView.getContext(), item.streamCount)); itemUploaderView.setVisibility(View.INVISIBLE); - PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView); + CoilHelper.INSTANCE.loadPlaylistThumbnail(itemThumbnailView, item.thumbnailUrl); if (item instanceof PlaylistDuplicatesEntry && ((PlaylistDuplicatesEntry) item).timesStreamIsContained > 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 765732063..f79f3c785 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.getName()); 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 bc39dafe6..ca626e704 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 49e72328e..49b1611be 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -86,8 +86,8 @@ import org.schabi.newpipe.databinding.PlayerBinding; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; -import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.Image; +import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -118,9 +118,9 @@ import org.schabi.newpipe.player.ui.VideoPlayerUi; import org.schabi.newpipe.util.DependentPreferenceHelper; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.StreamTypeUtil; +import org.schabi.newpipe.util.image.PicassoHelper; import java.util.List; import java.util.Optional; @@ -816,7 +816,7 @@ public final class Player implements PlaybackListener, Listener { cancelLoadingCurrentThumbnail(); // 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; 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 26065de15..c7c53b097 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; @@ -177,8 +178,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.loadBitmap(App.getApp(), 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 ec2bed67a..4e6830cb9 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -13,10 +13,9 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.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 coil.Coil; public class ContentSettingsFragment extends BasePreferenceFragment { private String youtubeRestrictedModeEnabledKey; @@ -42,14 +41,17 @@ 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) - .show(); - } catch (final IOException e) { - Log.e(TAG, "Unable to clear Picasso cache", e); + final var loader = Coil.imageLoader(preference.getContext()); + if (loader.getMemoryCache() != null) { + loader.getMemoryCache().clear(); } + if (loader.getDiskCache() != null) { + loader.getDiskCache().clear(); + } + Toast.makeText(preference.getContext(), + R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) + .show(); + 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 37335421d..c566313e3 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 36abef9e5..c340dca22 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,20 +154,15 @@ 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.name); holder.view.setOnClickListener(view -> clickedItem(position)); - PicassoHelper.loadPlaylistThumbnail(entry.thumbnailUrl).into(holder.thumbnailView); - - } else if (selectedItem instanceof PlaylistRemoteEntity) { - final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); - + CoilHelper.INSTANCE.loadPlaylistThumbnail(holder.thumbnailView, entry.thumbnailUrl); + } else if (selectedItem instanceof PlaylistRemoteEntity entry) { holder.titleView.setText(entry.getName()); 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/image/CoilHelper.kt b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt new file mode 100644 index 000000000..ce92341d5 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt @@ -0,0 +1,94 @@ +package org.schabi.newpipe.util.image + +import android.content.Context +import android.graphics.Bitmap +import android.widget.ImageView +import androidx.annotation.DrawableRes +import androidx.core.graphics.drawable.toBitmapOrNull +import coil.executeBlocking +import coil.imageLoader +import coil.request.ImageRequest +import org.schabi.newpipe.R +import org.schabi.newpipe.extractor.Image + +object CoilHelper { + fun loadBitmap(context: Context, url: String): Bitmap? { + val request = ImageRequest.Builder(context) + .data(url) + .build() + return context.imageLoader.executeBlocking(request).drawable?.toBitmapOrNull() + } + + 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 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) + .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) + .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 index 4b116bdf9..257f36133 100644 --- a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java @@ -25,7 +25,6 @@ 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; @@ -63,26 +62,6 @@ public final class PicassoHelper { .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); } @@ -91,53 +70,14 @@ public final class PicassoHelper { 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) + return loadThumbnail(images) .transform(new Transformation() { @Override public Bitmap transform(final Bitmap source) { From e6f01a2d19a775d22e26cdba003075dc6942fd2e Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 10:01:00 +0530 Subject: [PATCH 05/13] Clean up Picasso leftovers --- app/build.gradle | 2 - app/src/main/java/org/schabi/newpipe/App.java | 10 +- .../org/schabi/newpipe/about/AboutActivity.kt | 4 +- .../fragments/detail/VideoDetailFragment.java | 2 - .../list/channel/ChannelFragment.java | 7 +- .../list/playlist/PlaylistFragment.java | 6 +- .../java/org/schabi/newpipe/ktx/Bitmap.kt | 13 ++ .../org/schabi/newpipe/player/Player.java | 83 ++++----- .../settings/ContentSettingsFragment.java | 2 +- .../settings/DebugSettingsFragment.java | 9 - .../external_communication/ShareUtils.java | 21 +-- .../schabi/newpipe/util/image/CoilHelper.kt | 48 +++++ .../newpipe/util/image/PicassoHelper.java | 164 ------------------ 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 | 1 - app/src/main/res/values-bn/strings.xml | 1 - 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-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/settings_keys.xml | 1 - app/src/main/res/values/strings.xml | 2 - app/src/main/res/xml/debug_settings.xml | 7 - 74 files changed, 108 insertions(+), 386 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt delete mode 100644 app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java diff --git a/app/build.gradle b/app/build.gradle index 5275a9add..c7069401c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -269,8 +269,6 @@ dependencies { implementation "com.github.lisawray.groupie:groupie-viewbinding:${groupieVersion}" // Image loading - //noinspection GradleDependency --> 2.8 is the last version, not 2.71828! - implementation "com.squareup.picasso:picasso:2.8" implementation 'io.coil-kt:coil:2.6.0' // Markdown library for Android diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index 22ee3aa8b..5d92e90ac 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -23,9 +23,9 @@ 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 java.io.File; import java.io.IOException; import java.io.InterruptedIOException; import java.net.SocketException; @@ -113,12 +113,9 @@ public class App extends Application implements ImageLoaderFactory { // 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(); } @@ -131,13 +128,12 @@ public class App extends Application implements ImageLoaderFactory { .maxSizeBytes(10 * 1024 * 1024) .build()) .diskCache(() -> new DiskCache.Builder() + .directory(new File(getExternalCacheDir(), "coil")) .maxSizeBytes(50 * 1024 * 1024) .build()) .allowRgb565(true); - final var prefs = PreferenceManager.getDefaultSharedPreferences(this); - if (MainActivity.DEBUG - && prefs.getBoolean(getString(R.string.show_image_indicators_key), false)) { + if (MainActivity.DEBUG) { builder.logger(new DebugLogger()); } 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 7f148e9b5..0d0d0d48d 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt @@ -167,8 +167,8 @@ class AboutActivity : AppCompatActivity() { "https://square.github.io/okhttp/", StandardLicenses.APACHE2 ), SoftwareComponent( - "Picasso", "2013", "Square, Inc.", - "https://square.github.io/picasso/", StandardLicenses.APACHE2 + "Coil", "2023", "Coil Contributors", + "https://coil-kt.github.io/coil/", StandardLicenses.APACHE2 ), SoftwareComponent( "PrettyTime", "2012 - 2020", "Lincoln Baxter, III", 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 da4b071c3..96523321b 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 @@ -160,8 +160,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; 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 bab3cf907..2e225f879 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,12 +53,12 @@ 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 coil.util.CoilUtils; import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; @@ -73,7 +73,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; @@ -570,7 +569,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); } 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 09c9682c7..2722ab1b4 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 @@ -54,7 +54,6 @@ 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.CoilHelper; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.text.TextEllipsizer; import java.util.ArrayList; @@ -63,6 +62,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.stream.Collectors; +import coil.util.CoilUtils; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; @@ -72,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; @@ -277,7 +275,7 @@ public class PlaylistFragment extends BaseListInfoFragment " + 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(); - // 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 Coil. onThumbnailLoaded(null); @@ -823,20 +780,38 @@ public final class Player implements PlaybackListener, Listener { } // scale down the notification thumbnail for performance - PicassoHelper.loadScaledDownThumbnail(context, thumbnails) - .tag(PICASSO_PLAYER_THUMBNAIL_TAG) - .into(currentThumbnailTarget); - } + final var target = new Target() { + @Override + public void onError(@Nullable final Drawable 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); + } - private void cancelLoadingCurrentThumbnail() { - // cancel the Picasso job associated with the player thumbnail, if any - PicassoHelper.cancelTag(PICASSO_PLAYER_THUMBNAIL_TAG); + @Override + public void onStart(@Nullable final Drawable placeholder) { + if (DEBUG) { + Log.d(TAG, "Thumbnail - onStart() called"); + } + } + + @Override + public void onSuccess(@NonNull final Drawable 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(DrawableKt.toBitmapOrNull(result, result.getIntrinsicWidth(), + result.getIntrinsicHeight(), null)); + } + }; + CoilHelper.INSTANCE.loadScaledDownThumbnail(context, thumbnails, target); } 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/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 4e6830cb9..2cda1b4ea 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -49,7 +49,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { loader.getDiskCache().clear(); } Toast.makeText(preference.getContext(), - R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) + R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) .show(); 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 d78ade49d..c6abb5405 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 { findPreference(getString(R.string.allow_heap_dumping_key)); final Preference showMemoryLeaksPreference = findPreference(getString(R.string.show_memory_leaks_key)); - final Preference showImageIndicatorsPreference = - findPreference(getString(R.string.show_image_indicators_key)); final Preference checkNewStreamsPreference = findPreference(getString(R.string.check_new_streams_key)); final Preference crashTheAppPreference = @@ -38,7 +35,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment { assert allowHeapDumpingPreference != null; assert showMemoryLeaksPreference != null; - assert showImageIndicatorsPreference != null; assert checkNewStreamsPreference != null; assert crashTheAppPreference != null; assert showErrorSnackbarPreference != null; @@ -61,11 +57,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/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java index 7524e5413..3e93abf4d 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 @@ -24,8 +24,8 @@ import androidx.core.content.FileProvider; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.Image; +import org.schabi.newpipe.util.image.CoilHelper; import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; import java.io.File; import java.io.FileOutputStream; @@ -278,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, @@ -335,15 +335,7 @@ public final class ShareUtils { } /** - * Generate a {@link ClipData} with the image of the content shared, if it's in the app cache. - * - *

- * 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. - *

+ * Generate a {@link ClipData} with the image of the content shared. * *

* In order to display the image in the content preview of the Android share sheet, an URI of @@ -359,9 +351,8 @@ 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}. + * This method will call {@link CoilHelper#loadBitmap(Context, String)} to get the + * thumbnail of the content. *

* *

@@ -378,7 +369,7 @@ public final class ShareUtils { @NonNull final Context context, @NonNull final String thumbnailUrl) { try { - final Bitmap bitmap = PicassoHelper.getImageFromCacheIfPresent(thumbnailUrl); + final var bitmap = CoilHelper.INSTANCE.loadBitmap(context, thumbnailUrl); if (bitmap == null) { 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 index ce92341d5..3977212bf 100644 --- a/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt @@ -2,16 +2,25 @@ 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 androidx.core.graphics.drawable.toBitmapOrNull import coil.executeBlocking import coil.imageLoader import coil.request.ImageRequest +import coil.size.Size +import coil.target.Target +import coil.transform.Transformation +import org.schabi.newpipe.MainActivity import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Image +import org.schabi.newpipe.ktx.scale +import kotlin.math.min object CoilHelper { + private const val TAG = "CoilHelper" + fun loadBitmap(context: Context, url: String): Bitmap? { val request = ImageRequest.Builder(context) .data(url) @@ -35,6 +44,44 @@ object CoilHelper { loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video) } + fun loadScaledDownThumbnail(context: Context, images: List, target: Target) { + 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) + + 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)) + val copied = input.scale(notificationThumbnailWidth, newHeight) + input.recycle() + return copied + } else { + input.recycle() + return result + } + } + }) + .build() + + context.imageLoader.enqueue(request) + } + fun loadDetailsThumbnail(target: ImageView, images: List) { val url = ImageStrategy.choosePreferredImage(images) loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video, false) @@ -67,6 +114,7 @@ object CoilHelper { showPlaceholder: Boolean = true ) { val request = getImageRequest(target.context, url, placeholderResId, showPlaceholder) + .target(target) .build() target.context.imageLoader.enqueue(request) } 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 257f36133..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java +++ /dev/null @@ -1,164 +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.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 cancelTag(final Object tag) { - picassoInstance.cancelTag(tag); - } - - public static void setIndicatorsEnabled(final boolean enabled) { - picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging - } - - public static RequestCreator loadThumbnail(@NonNull final List images) { - return loadImageDefault(images, R.drawable.placeholder_thumbnail_video); - } - - public static RequestCreator loadScaledDownThumbnail(final Context context, - @NonNull final List images) { - // scale down the notification thumbnail for performance - return 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-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml index 077cf1106..290d9f6ab 100644 --- a/app/src/main/res/values-ar-rLY/strings.xml +++ b/app/src/main/res/values-ar-rLY/strings.xml @@ -302,7 +302,6 @@ %s مشارك جلب البيانات الوصفية… - إظهار مؤشرات الصور انقر للتنزيل %s تعطيل الوضع السريع , @@ -830,7 +829,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 82173d758..902fe8c3a 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -674,8 +674,6 @@ معاينة مصغرة على شريط التمرير وضع علامة على تمت مشاهدته أُعجب بها منشئ المحتوى - أظهر أشرطة ملونة لبيكاسو أعلى الصور تشير إلى مصدرها: الأحمر للشبكة والأزرق للقرص والأخضر للذاكرة - إظهار مؤشرات الصور اقتراحات البحث عن بعد اقتراحات البحث المحلية اسحب العناصر لإزالتها diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 66bfe75de..9c9c15c96 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -505,8 +505,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 dceaaf6c5..5ddf30c2a 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -630,8 +630,6 @@ Перайсці на вэбсайт Правядзіце пальцам па элементах, каб выдаліць іх Адмяніць пастаянную мініяцюру - Паказаць індыкатары выявы - Паказваць каляровыя стужкі Пікаса на выявах, якія пазначаюць іх крыніцу: чырвоная для сеткі, сіняя для дыска і зялёная для памяці Апрацоўка стужкі… Вам будзе прапанавана, дзе захоўваць кожную спампоўку Загрузка стужкі… diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index bc235446c..9b8c06bc5 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -539,7 +539,6 @@ Това видео е с възрастова граница. \n \nВключете „%1$s“ в настройките ако искате да го пуснете. - Показвай цветни Picasso-панделки в горната част на изображенията като индикатор за техния произход (червен – от мрежата, син – от диска и червен – от паметта) Автоматична (тази на устройството) Мащабиране на миниатюрата в известието от 16:9 към 1:1 формат (възможни са изкривявания) Избете плейлист diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 853b04b64..8fe38988a 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -589,7 +589,6 @@ নতুন ধারা কম্পাঙ্ক দেখো পূর্বদর্শন রেখার মাধ্যমে প্রাকদর্শন - ছবিরূপ সূচক দেখাও দেখিও না যেকোনো নেটওয়ার্ক পরেরটা ক্রমে রাখা হয়েছে diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 871af187b..dd08e3526 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -625,7 +625,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 els videos hi ha una pantalla negre o tartamudegen Mostra detalls del canal No s\'ha establert una carpeta de descàrregues, selecciona la carpeta per defecte ara @@ -669,7 +668,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 e6e375a4c..d14449cf1 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -631,8 +631,6 @@ پیشان نەدرێت کواڵێتی نزم (بچووکتر) کواڵێتی بەرز (گەورەتر) - پیشاندانی شریتە ڕەنگکراوەکانی پیکاسۆ لەسەرووی وێنەکانەوە بۆ بەدیار خستنی سەرچاوەکانیان : سوور بۆ تۆڕ ، شین بۆ دیسک و سەوز بۆ بیرگە - پیشاندانی دیارخەرەکانی وێنە پێشنیازکراوەکانی گەڕانی ڕیمۆت پێشنیازکراوەکانی گەڕانی نێوخۆیی دیارکردن وەک بینراو diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 34537446c..e6263f6e0 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -654,8 +654,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 601bc3752..cbb59d591 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -516,7 +516,6 @@ Behandler… Det kan tage et øjeblik Vis hukommelseslækager Deaktivér medietunneling - Vis billedindikatorer Netværkskrav Alle netværk Kontrolfrekvens @@ -695,7 +694,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å webside - 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 Pga. ExoPlayer-begrænsninger blev søgevarigheden sat til %d sekunder Vis kun ikke-grupperede abonnementer diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index fcc38bf0b..028ff86ca 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -637,8 +637,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 6e9525459..58a70c270 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -634,8 +634,6 @@ Προεπισκόπηση στην μπάρα αναζήτησης Σήμανση ως αναπαραχθέν Επισημάνθηκε από τον δημιουργό - Εμφάνιση χρωματιστής κορδέλας πάνω στις εικόνες, που δείχνει την πηγή τους: κόκκινη για δίκτυο, μπλε για δίσκο και πράσινο για μνήμη - Εμφάνιση δεικτών εικόνων Προτάσεις απομακρυσμένης αναζήτησης Προτάσεις τοπικής αναζήτησης diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c7b780a1f..a44c72937 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -647,8 +647,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 1d3fcdcb8..0e6e082c9 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -634,8 +634,6 @@ \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 6f94a8f62..076b50d10 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -641,8 +641,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 bilaketaren iradokizunak Tokiko bilaketa-iradokizunak Ikusi gisa markatu diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 2afeaf286..02ac369d7 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -597,7 +597,6 @@ رنگی کردن آگاهی گشودن با نشانه به عنوان دیده شده - نمایش روبان‌های رنگی پیکاسو در بالای تصویرها کهنشانگر منبعشان است: قرمز برای شبکه ، آبی برای دیسک و سبز برای حافظه درخواست از اندروید برای سفارشی‌سازی رنگ آگاهی براساس رنگ اصلی در بندانگشتی (توجّه داشته باشید که روی همهٔ افزاره‌ها در دسترس نیست) این ویدیو محدود به سن است. \n @@ -635,7 +634,6 @@ قلب‌شده به دست ایجادگر پیشنهادهای جست‌وجوی محلّی پیشنهادهای جست‌وجوی دوردست - نمایش نشانگرهای تصویر بارگیری پایان یافت %s بارگیری پایان یافتند diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 67350d7ba..d72402e77 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -634,8 +634,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 66418e7cf..f6a3d6285 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -646,10 +646,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 efe1b7f5e..4b087a882 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -627,7 +627,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 @@ -664,7 +663,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 befde2d53..1da62fc75 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -654,8 +654,6 @@ תמונה מוקטנת בסרגל הנגינה סומן בלב על ידי היוצר סימון כנצפה - הצגת סרטים בסגנון פיקאסו בראש התמונות לציון המקור שלהם: אדום זה מהרשת, כחול מהכונן וירוק מהזיכרון - הצגת מחווני תמונות הצעות חיפוש מרוחקות הצעות חיפוש מקומיות diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 51455fafb..6e185156d 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -570,7 +570,6 @@ कतारबद्ध हुआ स्ट्रीम विवरण लोड हो रहे हैं… प्रोसेस हो रहा है… कुछ समय लग सकता है - छवि संकेतक दिखाएं प्लेयर का उपयोग करते समय क्रैश विकल्प दिखाता है नई स्ट्रीमों के लिए जांच चलाएं एक त्रुटि स्नैकबार दिखाएं @@ -668,7 +667,6 @@ लीक-कैनरी उपलब्ध नहीं है एक त्रुटी हुई है, नोटीफिकेशन देखें यदि वीडियो प्लेबैक पर आप काली स्क्रीन या रुक-रुक कर वीडियो चलने का अनुभव करते हैं तो मीडिया टनलिंग को अक्षम करें। - छवियों के शीर्ष पर पिकासो रंगीन रिबन दिखाएँ जो उनके स्रोत को दर्शाता है: नेटवर्क के लिए लाल, डिस्क के लिए नीला और मेमोरी के लिए हरा त्रुटी की नोटीफिकेशन बनाएं इस डाउनलोड को पुनर्प्राप्त नहीं किया जा सकता अभी तक कोई डाउनलोड फ़ोल्डर सेट नहीं किया गया है, अब डिफ़ॉल्ट डाउनलोड फ़ोल्डर चुनें diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 419f4619e..3a945bba0 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -646,7 +646,6 @@ %s pruža ovaj razlog: Obrada u tijeku … Može malo potrajati Za ukljanjanje stavki povuci ih - Prikaži indikatore slike Preuzimanje je gotovo %s preuzimanja su gotova @@ -655,7 +654,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 5a402c94c..f86704148 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -570,7 +570,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 Lista legutóbbi frissítése: %s @@ -657,7 +656,6 @@ \nBiztos benne\? Ez nem vonható vissza! 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 71900400e..9a6472692 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -631,13 +631,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 ed5ebe99b..d921cfac0 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -657,8 +657,6 @@ Upprunalegir textar frá þjónustu verða sýnilegir í atriðum Slökkva á fjölmiðlagöngum Slökktu á fjölmiðlagöngum ef þú finnur fyrir svörtum skjá eða stami við spilun myndbandar - 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 „Hrynja spilara“ 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 2a5ac16d3..9d1c4bc0b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -644,8 +644,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 74924125a..7e7cf83a2 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -634,8 +634,6 @@ %s つのダウンロードが完了しました - ピカソは、画像の上に、画像の出所を識別する色彩記章を表示します: 赤はネットワーク、青はディスク、緑はメモリ - 画像に標識を表示 処理中… 少し時間がかかるかもしれません 新しいバージョンを手動で確認します アップデートを確認中… diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index ecb2a8495..4344c245d 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -384,7 +384,6 @@ ორიგინალური ტექსტები სერვისებიდან ხილული იქნება ნაკადის ერთეულებში მედია გვირაბის გათიშვა გამორთეთ მედია გვირაბი, თუ ვიდეოს დაკვრისას შავი ეკრანი ან ჭუჭყი გაქვთ - გამოსახულების ინდიკატორების ჩვენება აჩვენე \"დამკვრელის დამსხვრევა\" აჩვენებს ავარიის ვარიანტს დამკვრელის გამოყენებისას გაუშვით შემოწმება ახალი ნაკადებისთვის @@ -683,7 +682,6 @@ გამოწერების იმპორტი ვერ მოხერხდა შეატყობინეთ სასიცოცხლო ციკლის შეცდომებს იძულებითი მოხსენება შეუსაბამო Rx გამონაკლისების შესახებ ფრაგმენტის ან აქტივობის სასიცოცხლო ციკლის გარეთ განკარგვის შემდეგ - აჩვენეთ პიკასოს ფერადი ლენტები სურათების თავზე, სადაც მითითებულია მათი წყარო: წითელი ქსელისთვის, ლურჯი დისკისთვის და მწვანე მეხსიერებისთვის სწრაფი კვების რეჟიმი ამაზე მეტ ინფორმაციას არ იძლევა. „%s“-ის არხის ჩატვირთვა ვერ მოხერხდა. ხელმისაწვდომია ზოგიერთ სერვისში, როგორც წესი, ბევრად უფრო სწრაფია, მაგრამ შეიძლება დააბრუნოს შეზღუდული რაოდენობის ელემენტი და ხშირად არასრული ინფორმაცია (მაგ. ხანგრძლივობის გარეშე, ელემენტის ტიპი, არ არის ლაივის სტატუსი) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 939d97441..5d5dfa023 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -545,8 +545,6 @@ 미디어 터널링 비활성화 서비스의 원본 텍스트가 스트림 항목에 표시됩니다 동영상 재생 시 검은 화면이 나타나거나 끊김 현상이 발생하면 미디어 터널링을 비활성화하세요. - 이미지 표시기 표시 - 원본을 나타내는 이미지 위에 피카소 컬러 리본 표시: 네트워크는 빨간색, 디스크는 파란색, 메모리는 녹색 \"플레이어 충돌\" 표시 플레이어를 사용할 때 충돌 옵션을 표시합니다 새로운 스트림 확인 실행 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index c68e49bdd..2294a357d 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -644,8 +644,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 58b9a9d76..a28d3e607 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -625,12 +625,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 Pārvelciet objektus, lai tos noņemtu Lokālie meklēšanas ieteikumi - Rādīt attēlu indikatorus Augstas kvalitātes (lielāks) Pārbaudīt atjauninājumus Manuāli pārbaudīt, vai ir atjauninājumi diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index c439593f7..ee4b88a41 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -595,7 +595,6 @@ രണ്ടാം പ്രവർത്തന ബട്ടൺ ആദ്യ പ്രവർത്തന ബട്ടൺ വീഡിയോ കാണുമ്പോൾ കറുത്ത സ്ക്രീൻ, അവ്യക്തത അനുഭവിക്കുന്നു എങ്കിൽ മീഡിയ ട്യൂൺലിങ് പ്രവർത്തനരഹിതമാക്കുക - ഉറവിടം തിരിച്ചറിയാൻ പിക്കാസോ കളർഡ് റിബൺ ചിത്രങ്ങളുടെ മുകളിൽ കാണിക്കുക: നെറ്റ്‌വർക്കിന് ചുവപ്പ്, ഡിസ്കിനു നീല, മെമ്മറിയിക്ക് പച്ച സീക്ബാർ ചെറുചിത്രം പ്രദർശനം സ്നേഹത്തോടെ സൃഷ്ടാവ് ഡിസ്ക്രിപ്ഷനിലെ ടെക്സ്റ്റ്‌ സെലക്ട്‌ ചെയ്യുവാൻ അനുവദിക്കാതെ ഇരിക്കുക @@ -630,7 +629,6 @@ കാണിക്കരുത് കുറഞ്ഞ നിലവാരം (ചെറുത് ) ഉയർന്ന നിലവാരം (വലിയത് ) - ഇമേജ് ഇൻഡിക്കേറ്ററുകൾ കാണിക്കുക മീഡിയ ട്യൂൺലിങ് പ്രവർത്തനരഹിതമാക്കുക ഡൌൺലോഡ് ഫോൾഡർ ഇത് വരെയും സെറ്റ് ചെയ്തിട്ടില്ല, സ്ഥിര ഡൌൺലോഡ് ഫോൾഡർ ഇപ്പോൾ തിരഞ്ഞെക്കുക അഭിപ്രായങ്ങൾ പ്രവർത്തനരഹിതമായിരിക്കുന്നു diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 416ebfd02..7acf7f4cd 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -644,9 +644,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 637eb1751..c744de62c 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -611,7 +611,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 b4629a03f..26e328ed4 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -632,8 +632,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 94e7ae2f4..f1960b980 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -440,8 +440,6 @@ ߗߋߢߊߟߌ ߟߎ߬ ߞߟߏߜߍ߫ ߓߐߛߎ߲ߡߊ ߟߎ߬ ߦߌ߬ߘߊ߬ߕߐ߫ ߟߋ߬ ߟߊ߬ߖߍ߲߬ߛߍ߲߬ߠߌ߲ ߘߐ߫ ߞߋߟߋߞߋߟߋ ߟߊ߫ ߝߊߟߊ߲ߓߍߦߊ ߟߊߛߊ߬ ߞߋߟߋߞߋߟߋ ߟߊ߫ ߝߊߟߊ߲ߓߍߦߊ ߟߊߛߊ߬ ߣߴߌ ߞߊ߬ ߥߊ߲߬ߊߥߊ߲߬ ߝߌ߲ ߦߋ߫ ߥߟߊ߫ ߜߊߘߊ߲ߜߊߘߊ߲ߠߌ߲ ߦߋߡߍ߲ߕߊ ߘߏ߫ ߘߐߛߊߙߌ߫ ߕߎߡߊ - ߞߊ߬ ߖߌ߬ߦߊ߬ߓߍ߫ ߦߌ߬ߘߊ߬ߟߊ߲ ߠߎ߫ ߝߍ߲߬ߛߍ߲߫ - ߏ߬ ߦߋ߫ ߔߌߛߊߞߏ߫ ߟߊ߫ ߡߙߎߝߋ߫ ߞߟߐ߬ߡߊ ߟߎ߫ ߟߋ߬ ߝߍ߲߬ߛߍ߲߬ ߠߊ߫ ߖߌ߬ߦߊ߬ߓߍ ߟߎ߫ ߞߎ߲߬ߘߐ߫ ߞߵߊ߬ߟߎ߬ ߓߐߛߎ߲ ߦߌ߬ߘߊ߬: ߥߎߟߋ߲߬ߡߊ߲ ߦߋ߫ ߞߙߏ߬ߝߏ ߕߊ ߘߌ߫߸ ߓߊ߯ߡߊ ߦߋ߫ ߝߘߍ߬ ߜߍߟߍ߲ ߕߊ ߘߌ߫ ߊ߬ߣߌ߫ ߝߙߌߛߌߡߊ ߦߋ߫ ߦߟߌߕߏߟߊ߲ ߕߊ ߘߌ߫ ߟߊ߬ߓߐ߬ߟߌ ߦߴߌߘߐ߫… ߞߵߊ߬ ߘߊߡߌ߬ߣߊ߬ ߞߊ߲߬ߞߎߡߊ ߟߎ߬ ߟߊߛߊ߬ߣߍ߲ ߠߋ߬ diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index f9faa8324..59a6a739a 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -342,7 +342,6 @@ ସେବାଗୁଡିକରୁ ମୂଳ ଲେଖା ଷ୍ଟ୍ରିମ୍ ଆଇଟମ୍ ଗୁଡିକରେ ଦୃଶ୍ୟମାନ ହେବ ମିଡିଆ ଟନେଲିଂକୁ ଅକ୍ଷମ କରନ୍ତୁ ଯଦି ଆପଣ ଏକ କଳା ପରଦା ଅନୁଭବ କରନ୍ତି କିମ୍ବା ଭିଡିଓ ପ୍ଲେବେକ୍ ଉପରେ ଝୁଣ୍ଟି ପଡ଼ନ୍ତି ତେବେ ମିଡିଆ ଟନେଲିଂକୁ ଅକ୍ଷମ କରନ୍ତୁ । - ଚିତ୍ରଗୁଡ଼ିକର ଉପରେ ପିକାସୋ ରଙ୍ଗୀନ ଫିତା ଦେଖାନ୍ତୁ: ସେମାନଙ୍କର ଉତ୍ସକୁ ସୂଚାଇଥାଏ: ନେଟୱାର୍କ ପାଇଁ ନାଲି, ଡିସ୍କ ପାଇଁ ନୀଳ ଏବଂ ସ୍ମୃତି ପାଇଁ ସବୁଜ ଆମଦାନି କରନ୍ତୁ ଠାରୁ ଆମଦାନୀ କରନ୍ତୁ ଆମଦାନି… @@ -657,7 +656,6 @@ ଅଟୋ-ଜେନେରେଟ୍ (କୌଣସି ଅପଲୋଡର୍ ମିଳିଲା ନାହିଁ) ପୁରଣ କରନ୍ତୁ କ୍ୟାପସନ୍ - ପ୍ରତିଛବି ସୂଚକ ଦେଖାନ୍ତୁ ପ୍ଲେୟାର ବ୍ୟବହାର କରିବା ସମୟରେ ଏକ କ୍ରାସ୍ ବିକଳ୍ପ ଦେଖାଏ ନୂତନ ଷ୍ଟ୍ରିମ୍ ପାଇଁ ଯାଞ୍ଚ ଚଲାନ୍ତୁ ଆପ୍ କ୍ରାସ୍ କରନ୍ତୁ diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 814cdb885..d612ff75b 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -626,8 +626,6 @@ ਨਿਊਪਾਈਪ ਖਾਮੀ ਤੋਂ ਪ੍ਰਭਾਵਤ ਹੋਈ ਹੈ, ਇੱਥੇ ਨੱਪ ਕੇ ਰਿਪੋਰਟ ਕਰੋ ਇੱਕ ਖਾਮੀ ਪ੍ਰਭਾਵੀ ਹੋਈ ਹੈ, ਨੋਟੀਫੀਕੇਸ਼ਨ ਵੇਖੋ ਆਈਟਮਾਂ ਨੂੰ ਇੱਕ ਪਾਸੇ ਖਿੱਚ ਕੇ ਹਟਾਓ - ਦ੍ਰਿਸ਼ ਸੂਚਕ ਵਿਖਾਓ - ਦ੍ਰਿਸ਼ਾਂ ਦੇ ਉੱਪਰ ਉਹਨਾਂ ਦੀ ਸਰੋਤ-ਪਛਾਣ ਲਈ ਪਿਕਾਸੋ ਦੇ ਰੰਗਦਾਰ ਰਿਬਨ ਵਿਖਾਓ : ਨੈੱਟਵਰਕ ਲਈ ਲਾਲ, ਡਿਸਕ ਲਈ ਨੀਲੇ ਤੇ ਮੈਮਰੀ ਲਈ ਹਰੇ ਨਵੀਂ ਸਟ੍ਰੀਮ ਦੇ ਨੋਟੀਫਿਕੇਸ਼ਨ ਪਿੰਨ ਕੀਤੀ ਟਿੱਪਣੀ ਅੱਪਡੇਟ ਦੀ ਉਪਲੱਬਧਤਾ ਪਰਖੀ ਜਾ ਰਹੀ… diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 01f242cd9..b7c517ffc 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -649,8 +649,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 5a203e0f2..3d4b57621 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -644,7 +644,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 @@ -655,7 +654,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 498a49a53..23310adec 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -644,8 +644,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 5534dbf0b..99e5cbd04 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -644,8 +644,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 bcef4d952..a5eb21143 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -660,8 +660,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 9e79b165f..667e5413d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -652,8 +652,6 @@ Миниатюра над полосой прокрутки Автору видео понравилось это Пометить проигранным - Picasso: указать цветом источник изображений (красный — сеть, синий — диск, зелёный — память) - Цветные метки на изображениях Серверные предложения поиска Локальные предложения поиска diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml index 5a4f35de5..e970950e0 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -646,8 +646,6 @@ %sちぬダウンロードぬかんりょうさびたん %sちぬダウンロードぬかんりょうさびたん - ピカソー、がぞうぬういに、がぞうくとぅどぅくるしーきびちするしきさいきしーょうひょうじさびーん: あかーネットワーク、あおーディスク、みどぅれーメモリ - やしがぞうんかいふぃいょうしきひょうじ しーょりちゅう… くーてーんじがんがかかいんかむしりやびらん みーさるバージョンしーゅどうでぃかくにんさびーん アップデートかくにんちゅう… diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index a5959086e..9ede53a76 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -270,7 +270,6 @@ LeakCanary ᱵᱟᱭ ᱧᱟᱢᱚᱜ ᱠᱟᱱᱟ ᱢᱮᱢᱚᱨᱤ ᱞᱤᱠᱟᱞ ᱢᱚᱱᱤᱴᱚᱨᱤᱝ ᱦᱤᱯ ᱰᱟᱢᱯᱤᱝ ᱚᱠᱛᱚ ᱨᱮ ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱨᱟᱥᱴᱨᱤᱭ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱡᱤᱭᱚᱱ ᱪᱤᱠᱤ ᱠᱷᱚᱱ ᱵᱟᱦᱨᱮ ᱨᱮ ᱵᱷᱮᱜᱟᱨ ᱠᱚ ᱚᱱᱚᱞ ᱢᱮ - ᱱᱮᱴᱣᱟᱨᱠ ᱞᱟᱹᱜᱤᱫ red, ᱰᱤᱥᱠ ᱞᱟᱹᱜᱤᱫ blue ᱟᱨ ᱢᱮᱢᱚᱨᱤ ᱞᱟᱹᱜᱤᱫ green ᱯᱞᱮᱭᱟᱨ ᱵᱮᱵᱷᱟᱨ ᱚᱠᱛᱮ ᱨᱮ ᱠᱨᱟᱥ ᱚᱯᱥᱚᱱ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ ᱤᱢᱯᱳᱨᱴ ᱤᱢᱯᱚᱨᱴ @@ -540,7 +539,6 @@ ᱡᱤᱱᱤᱥ ᱠᱚᱨᱮᱱᱟᱜ ᱢᱩᱞ ᱚᱠᱛᱚ ᱧᱮᱞ ᱢᱮ ᱥᱮᱵᱟ ᱠᱷᱚᱱ ᱚᱨᱡᱤᱱᱤᱭᱟᱞ ᱴᱮᱠᱥᱴ ᱠᱚ ᱥᱴᱨᱤᱢ ᱤᱴᱮᱢ ᱨᱮ ᱧᱮᱞᱚᱜᱼᱟ ᱡᱩᱫᱤ ᱟᱢ ᱵᱷᱤᱰᱤᱭᱳ ᱯᱞᱮᱭᱚᱯ ᱨᱮ ᱵᱞᱮᱠ ᱥᱠᱨᱤᱱ ᱟᱨᱵᱟᱝ ᱠᱷᱟᱹᱞᱤ ᱥᱴᱮᱴᱞᱤᱝ ᱮᱢ ᱧᱟᱢᱟ ᱮᱱᱠᱷᱟᱱ ᱢᱤᱰᱤᱭᱟ ᱴᱩᱱᱮᱞᱤᱝ ᱵᱚᱫᱚᱞ ᱢᱮ ᱾ - ᱪᱤᱛᱟᱹᱨ ᱪᱤᱱᱦᱟᱹ ᱠᱚ ᱧᱮᱞ ᱢᱮ ᱱᱟᱣᱟ ᱥᱴᱨᱤᱢ ᱞᱟᱹᱜᱤᱫ ᱪᱟᱪᱞᱟᱣ ᱢᱮ ᱢᱤᱫ error notification ᱛᱮᱭᱟᱨ ᱢᱮ ᱥᱮᱞᱮᱫ ᱮᱠᱥᱯᱳᱨᱴ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 6b89b32c3..fce5280e1 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -634,8 +634,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 e3aa5b6bc..5c4210b71 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -653,8 +653,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 e37c3a36d..72e661a1b 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -633,8 +633,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 5f14a0e51..2300cd929 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -644,7 +644,6 @@ Означи као одгледано Коментари су онемогућени Обрађивање… Може потрајати пар тренутака - Прикажи индикаторе слике Не покрећите видео снимке у мини-плејеру, већ директно пређите на режим целог екрана, ако је аутоматска ротација закључана. И даље можете приступити мини-плејеру тако што ћете изаћи из целог екрана Покрени главни плејер преко целог екрана Срушите плејер @@ -740,7 +739,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 8cf57f605..50948fd57 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -633,7 +633,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 @@ -651,7 +650,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-te/strings.xml b/app/src/main/res/values-te/strings.xml index b2846823c..90708580a 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -442,7 +442,6 @@ ప్లేబ్యాక్ స్పీడ్ నియంత్రణలు ఏమిలేదు మీరు బ్లాక్ స్క్రీన్ లేదా చలనచిత్రం ప్లేబ్యాక్‌లో అంతరాయాన్ని అనుభవిస్తే మీడియా టన్నెలింగ్‌ను నిలిపివేయండి - చిత్రాల మూలాన్ని సూచించే విధంగా వాటి పైభాగంలో పికాసో రంగు రిబ్బన్‌లను చూపండి: నెట్‌వర్క్ కోసం ఎరుపు, డిస్క్ కోసం నీలం మరియు మెమరీ కోసం ఆకుపచ్చ లోపం స్నాక్‌బార్‌ని చూపండి మీరు NewPipe యొక్క తాజా సంస్కరణను అమలు చేస్తున్నారు NewPipe నవీకరణ అందుబాటులో ఉంది! @@ -455,7 +454,6 @@ తక్కువ నాణ్యత (చిన్నది) చూపించవద్దు మీడియా టన్నెలింగ్‌ని నిలిపివేయండి - చిత్ర సూచికలను చూపు కొత్త స్ట్రీమ్‌ల కోసం తనిఖీని అమలు చేయండి ఎర్రర్ నోటిఫికేషన్‌ను సృష్టించండి దిగుమతి diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index adecbf3ff..29933bdc6 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -633,8 +633,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 c34708609..86350d40e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -646,8 +646,6 @@ Мініатюра з попереднім переглядом на повзунку поступу Вподобано автором Позначити переглянутим - Показувати кольорові стрічки Пікассо поверх зображень із зазначенням їх джерела: червоний для мережі, синій для диска та зелений для пам’яті - Показати індикатори зображень Віддалені пропозиції пошуку Локальні пошукові пропозиції diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 0d71173af..15f5a86d4 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -624,8 +624,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 fa8d31022..7b303621b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -624,8 +624,6 @@ 高品质(较大) 被创作者喜爱 标记为已观看 - 在图像顶部显示毕加索彩带,指示其来源:红色代表网络,蓝色代表磁盘,绿色代表内存 - 显示图像指示器 远程搜索建议 本地搜索建议 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 518a1290f..9f581b43f 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -668,9 +668,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 c404edeca..a4ad3578f 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -624,8 +624,6 @@ 拖動列縮圖預覽 被創作者加心號 標記為已觀看 - 在圖片頂部顯示畢卡索彩色絲帶,指示其來源:紅色代表網路、藍色代表磁碟、綠色代表記憶體 - 顯示圖片指示器 遠端搜尋建議 本機搜尋建議 diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index fb68a464d..e31cebb92 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/values/strings.xml b/app/src/main/res/values/strings.xml index 56140441c..bff35e5d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -486,8 +486,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 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" /> - - Date: Sat, 22 Jun 2024 10:45:04 +0530 Subject: [PATCH 06/13] Enable RGB-565 for low-end devices --- app/src/main/java/org/schabi/newpipe/App.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index 5d92e90ac..68ae0ca53 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -1,5 +1,6 @@ package org.schabi.newpipe; +import android.app.ActivityManager; import android.app.Application; import android.content.Context; import android.content.SharedPreferences; @@ -8,6 +9,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationManagerCompat; +import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import com.jakewharton.processphoenix.ProcessPhoenix; @@ -123,6 +125,8 @@ public class App extends Application implements ImageLoaderFactory { @NonNull @Override public ImageLoader newImageLoader() { + final var isLowRamDevice = ContextCompat.getSystemService(this, ActivityManager.class) + .isLowRamDevice(); final var builder = new ImageLoader.Builder(this) .memoryCache(() -> new MemoryCache.Builder(this) .maxSizeBytes(10 * 1024 * 1024) @@ -131,7 +135,7 @@ public class App extends Application implements ImageLoaderFactory { .directory(new File(getExternalCacheDir(), "coil")) .maxSizeBytes(50 * 1024 * 1024) .build()) - .allowRgb565(true); + .allowRgb565(isLowRamDevice); if (MainActivity.DEBUG) { builder.logger(new DebugLogger()); From 8ecd6e6b982026917d0f43081f67633a0f9fb1e5 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 16:24:52 +0530 Subject: [PATCH 07/13] Fix issue with background player --- .../newpipe/database/DatabaseMigrationTest.kt | 12 +++++------ .../local/history/HistoryRecordManagerTest.kt | 16 +++++++------- .../playlist/LocalPlaylistManagerTest.kt | 2 +- .../org/schabi/newpipe/database/BasicDAO.kt | 2 +- .../newpipe/database/stream/dao/StreamDAO.kt | 21 +++++-------------- .../local/history/HistoryRecordManager.java | 12 +++++------ .../local/playlist/LocalPlaylistManager.java | 4 ++-- .../local/subscription/SubscriptionManager.kt | 4 ++-- .../schabi/newpipe/util/SparseItemUtil.java | 2 +- 9 files changed, 32 insertions(+), 43 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt index a34cfece6..a46b6e36a 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -129,7 +129,7 @@ class DatabaseMigrationTest { ) val migratedDatabaseV3 = getMigratedDatabase() - val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst() + val listFromDB = migratedDatabaseV3.streamDAO().getAll().blockingFirst() // Only expect 2, the one with the null url will be ignored assertEquals(2, listFromDB.size) @@ -217,7 +217,7 @@ class DatabaseMigrationTest { ) val migratedDatabaseV8 = getMigratedDatabase() - val listFromDB = migratedDatabaseV8.searchHistoryDAO().all.blockingFirst() + val listFromDB = migratedDatabaseV8.searchHistoryDAO().getAll().blockingFirst() assertEquals(2, listFromDB.size) assertEquals("abc", listFromDB[0].search) @@ -283,8 +283,8 @@ class DatabaseMigrationTest { ) val migratedDatabaseV9 = getMigratedDatabase() - var localListFromDB = migratedDatabaseV9.playlistDAO().all.blockingFirst() - var remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().all.blockingFirst() + var localListFromDB = migratedDatabaseV9.playlistDAO().getAll().blockingFirst() + var remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().getAll().blockingFirst() assertEquals(1, localListFromDB.size) assertEquals(localUid2, localListFromDB[0].uid) @@ -303,8 +303,8 @@ class DatabaseMigrationTest { ) ) - localListFromDB = migratedDatabaseV9.playlistDAO().all.blockingFirst() - remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().all.blockingFirst() + localListFromDB = migratedDatabaseV9.playlistDAO().getAll().blockingFirst() + remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().getAll().blockingFirst() assertEquals(2, localListFromDB.size) assertEquals(localUid3, localListFromDB[1].uid) assertEquals(-1, localListFromDB[1].displayIndex) diff --git a/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt b/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt index 24be0f868..d04e56004 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt @@ -41,7 +41,7 @@ class HistoryRecordManagerTest { // For some reason the Flowable returned by getAll() never completes, so we can't assert // that the number of Lists it returns is exactly 1, we can only check if the first List is // correct. Why on earth has a Flowable been used instead of a Single for getAll()?!? - val entities = database.searchHistoryDAO().all.blockingFirst() + val entities = database.searchHistoryDAO().getAll().blockingFirst() assertThat(entities).hasSize(1) assertThat(entities[0].id).isEqualTo(1) assertThat(entities[0].serviceId).isEqualTo(0) @@ -59,25 +59,25 @@ class HistoryRecordManagerTest { // make sure all 4 were inserted database.searchHistoryDAO().insertAll(entries) - assertThat(database.searchHistoryDAO().all.blockingFirst()).hasSameSizeAs(entries) + assertThat(database.searchHistoryDAO().getAll().blockingFirst()).hasSameSizeAs(entries) // try to delete only "A" entries, "B" entries should be untouched manager.deleteSearchHistory("A").test().await().assertValue(2) - val entities = database.searchHistoryDAO().all.blockingFirst() + val entities = database.searchHistoryDAO().getAll().blockingFirst() assertThat(entities).hasSize(2) assertThat(entities).usingElementComparator { o1, o2 -> if (o1.hasEqualValues(o2)) 0 else 1 } .containsExactly(*entries.subList(2, 4).toTypedArray()) // assert that nothing happens if we delete a search query that does exist in the db manager.deleteSearchHistory("A").test().await().assertValue(0) - val entities2 = database.searchHistoryDAO().all.blockingFirst() + val entities2 = database.searchHistoryDAO().getAll().blockingFirst() assertThat(entities2).hasSize(2) assertThat(entities2).usingElementComparator { o1, o2 -> if (o1.hasEqualValues(o2)) 0 else 1 } .containsExactly(*entries.subList(2, 4).toTypedArray()) // delete all remaining entries manager.deleteSearchHistory("B").test().await().assertValue(2) - assertThat(database.searchHistoryDAO().all.blockingFirst()).isEmpty() + assertThat(database.searchHistoryDAO().getAll().blockingFirst()).isEmpty() } @Test @@ -90,11 +90,11 @@ class HistoryRecordManagerTest { // make sure all 3 were inserted database.searchHistoryDAO().insertAll(entries) - assertThat(database.searchHistoryDAO().all.blockingFirst()).hasSameSizeAs(entries) + assertThat(database.searchHistoryDAO().getAll().blockingFirst()).hasSameSizeAs(entries) // should remove everything manager.deleteCompleteSearchHistory().test().await().assertValue(entries.size) - assertThat(database.searchHistoryDAO().all.blockingFirst()).isEmpty() + assertThat(database.searchHistoryDAO().getAll().blockingFirst()).isEmpty() } private fun insertShuffledRelatedSearches(relatedSearches: Collection) { @@ -107,7 +107,7 @@ class HistoryRecordManagerTest { // make sure all entries were inserted assertEquals( relatedSearches.size, - database.searchHistoryDAO().all.blockingFirst().size + database.searchHistoryDAO().getAll().blockingFirst().size ) } diff --git a/app/src/androidTest/java/org/schabi/newpipe/local/playlist/LocalPlaylistManagerTest.kt b/app/src/androidTest/java/org/schabi/newpipe/local/playlist/LocalPlaylistManagerTest.kt index c392d8d3d..ce3aeb84a 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/local/playlist/LocalPlaylistManagerTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/local/playlist/LocalPlaylistManagerTest.kt @@ -72,6 +72,6 @@ class LocalPlaylistManagerTest { val result = manager.createPlaylist("name", listOf(stream, upserted)) result.test().await().assertComplete() - database.streamDAO().all.test().awaitCount(1).assertValue(listOf(stream, upserted)) + database.streamDAO().getAll().test().awaitCount(1).assertValue(listOf(stream, upserted)) } } diff --git a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt index 3f35d0ff9..4bb9e1fba 100644 --- a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.kt @@ -28,5 +28,5 @@ interface BasicDAO { /* Updates */ @Update - suspend fun update(entity: Entity): Int + fun update(entity: Entity): Int } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt index c7f05a1aa..354baaedb 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt @@ -34,10 +34,7 @@ interface StreamDAO : BasicDAO { fun setUploaderUrl(serviceId: Long, url: String, uploaderUrl: String): Completable @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun silentInsertInternal(stream: StreamEntity): Long - - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun silentInsertAllInternal(streams: List): List + fun silentInsertInternal(stream: StreamEntity): Long @Query("SELECT COUNT(*) != 0 FROM streams WHERE url = :url AND service_id = :serviceId") suspend fun exists(serviceId: Int, url: String): Boolean @@ -48,10 +45,10 @@ interface StreamDAO : BasicDAO { FROM streams WHERE url = :url AND service_id = :serviceId """ ) - suspend fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed? + fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed? @Transaction - suspend fun upsert(newerStream: StreamEntity): Long { + fun upsert(newerStream: StreamEntity): Long { val uid = silentInsertInternal(newerStream) if (uid != -1L) { @@ -65,20 +62,12 @@ interface StreamDAO : BasicDAO { return newerStream.uid } - fun upsertBlocking(newerStream: StreamEntity) = runBlocking { - upsert(newerStream) - } - @Transaction - suspend fun upsertAll(streams: List): List { + fun upsertAll(streams: List): List { return streams.map { upsert(it) } } - fun upsertAllBlocking(streams: List) = runBlocking { - upsertAll(streams) - } - - private suspend fun compareAndUpdateStream(newerStream: StreamEntity) { + private fun compareAndUpdateStream(newerStream: StreamEntity) { val existentMinimalStream = getMinimalStreamForCompare(newerStream.serviceId, newerStream.url) ?: throw IllegalStateException("Stream cannot be null just after insertion.") newerStream.uid = existentMinimalStream.uid diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index 1689d74d6..ce64ad57c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -110,10 +110,10 @@ public class HistoryRecordManager { .subscribeOn(Schedulers.io()) .blockingGet(); duration = completeInfo.getDuration(); - streamId = streamTable.upsertBlocking(new StreamEntity(completeInfo)); + streamId = streamTable.upsert(new StreamEntity(completeInfo)); } else { duration = info.getDuration(); - streamId = streamTable.upsertBlocking(new StreamEntity(info)); + streamId = streamTable.upsert(new StreamEntity(info)); } // Update the stream progress to the full duration of the video @@ -141,7 +141,7 @@ public class HistoryRecordManager { final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); return Maybe.fromCallable(() -> database.runInTransaction(() -> { - final long streamId = streamTable.upsertBlocking(new StreamEntity(info)); + final long streamId = streamTable.upsert(new StreamEntity(info)); final StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId); if (latestEntry != null) { @@ -236,7 +236,7 @@ public class HistoryRecordManager { public Maybe loadStreamState(final PlayQueueItem queueItem) { return queueItem.getStream() - .map(info -> streamTable.upsertBlocking(new StreamEntity(info))) + .map(info -> streamTable.upsert(new StreamEntity(info))) .flatMapPublisher(streamStateTable::getState) .firstElement() .flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0))) @@ -245,7 +245,7 @@ public class HistoryRecordManager { } public Maybe loadStreamState(final StreamInfo info) { - return Single.fromCallable(() -> streamTable.upsertBlocking(new StreamEntity(info))) + return Single.fromCallable(() -> streamTable.upsert(new StreamEntity(info))) .flatMapPublisher(streamStateTable::getState) .firstElement() .flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0))) @@ -255,7 +255,7 @@ public class HistoryRecordManager { public Completable saveStreamState(@NonNull final StreamInfo info, final long progressMillis) { return Completable.fromAction(() -> database.runInTransaction(() -> { - final long streamId = streamTable.upsertBlocking(new StreamEntity(info)); + final long streamId = streamTable.upsert(new StreamEntity(info)); final StreamStateEntity state = new StreamStateEntity(streamId, progressMillis); if (state.isValid(info.getDuration())) { streamStateTable.upsert(state); diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java index 2b6b9fee1..dd9307675 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java @@ -46,7 +46,7 @@ public class LocalPlaylistManager { // Make sure the new playlist is always on the top of bookmark. // The index will be reassigned to non-negative number in BookmarkFragment. return Maybe.fromCallable(() -> database.runInTransaction(() -> { - final List streamIds = streamTable.upsertAllBlocking(streams); + final List streamIds = streamTable.upsertAll(streams); final PlaylistEntity newPlaylist = new PlaylistEntity(name, false, streamIds.get(0), -1); @@ -61,7 +61,7 @@ public class LocalPlaylistManager { return playlistStreamTable.getMaximumIndexOf(playlistId) .firstElement() .map(maxJoinIndex -> database.runInTransaction(() -> { - final List streamIds = streamTable.upsertAllBlocking(streams); + final List streamIds = streamTable.upsertAll(streams); return insertJoinEntities(playlistId, streamIds, maxJoinIndex + 1); } )).subscribeOn(Schedulers.io()); diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt index 87d36746e..a206b8477 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt @@ -81,7 +81,7 @@ class SubscriptionManager(context: Context) { info.description, info.subscriberCount ) - runBlocking { subscriptionTable.update(it) } + subscriptionTable.update(it) } } @@ -90,7 +90,7 @@ class SubscriptionManager(context: Context) { .flatMapCompletable { entity: SubscriptionEntity -> Completable.fromAction { entity.notificationMode = mode - runBlocking { subscriptionTable().update(entity) } + subscriptionTable().update(entity) }.apply { if (mode != NotificationMode.DISABLED) { // notifications have just been enabled, mark all streams as "old" diff --git a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java index 4a17b27eb..6e9ea7a47 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java +++ b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java @@ -108,7 +108,7 @@ public final class SparseItemUtil { .subscribe(result -> { // save to database in the background (not on main thread) Completable.fromAction(() -> NewPipeDatabase.getInstance(context) - .streamDAO().upsertBlocking(new StreamEntity(result))) + .streamDAO().upsert(new StreamEntity(result))) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .doOnError(throwable -> From 5887438ddeb66b759fb62cd37e9220ca7a81bc91 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 17:07:04 +0530 Subject: [PATCH 08/13] Fix tests --- .../org/schabi/newpipe/database/FeedDAOTest.kt | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt index 893ae82b7..2d8138def 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt @@ -3,7 +3,7 @@ package org.schabi.newpipe.database import android.content.Context import androidx.room.Room import androidx.test.core.app.ApplicationProvider -import io.reactivex.rxjava3.core.Single +import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -22,7 +22,6 @@ import org.schabi.newpipe.extractor.channel.ChannelInfo import org.schabi.newpipe.extractor.stream.StreamType import java.io.IOException import java.time.OffsetDateTime -import kotlin.streams.toList class FeedDAOTest { private lateinit var db: AppDatabase @@ -94,17 +93,13 @@ class FeedDAOTest { ) } - private fun setupUnlinkDelete(time: String) { + private fun setupUnlinkDelete(time: String) = runBlocking { clearAndFillTables() - Single.fromCallable { - feedDAO.unlinkStreamsOlderThan(OffsetDateTime.parse(time)) - }.blockingSubscribe() - Single.fromCallable { - streamDAO.deleteOrphans() - }.blockingSubscribe() + feedDAO.unlinkStreamsOlderThan(OffsetDateTime.parse(time)) + streamDAO.deleteOrphans() } - private fun clearAndFillTables() { + private suspend fun clearAndFillTables() { db.clearAllTables() streamDAO.insertAll(allStreams) subscriptionDAO.insertAll( From 4affc948fe8141630186d493d4502dc218ab8269 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 17:46:39 +0530 Subject: [PATCH 09/13] Revert Upsert changes --- .../playlist/dao/PlaylistRemoteDAO.java | 33 ++++++++++++++----- .../database/stream/dao/StreamStateDAO.java | 20 +++++++---- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java index cb9c0bbdd..43ac47ca8 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java @@ -1,9 +1,15 @@ package org.schabi.newpipe.database.playlist.dao; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_DISPLAY_INDEX; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL; + import androidx.room.Dao; import androidx.room.Query; +import androidx.room.Transaction; import androidx.room.Update; -import androidx.room.Upsert; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; @@ -12,12 +18,6 @@ import java.util.List; import io.reactivex.rxjava3.core.Flowable; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_DISPLAY_INDEX; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL; - @Dao public interface PlaylistRemoteDAO extends BasicDAO { @Update @@ -48,8 +48,23 @@ public interface PlaylistRemoteDAO extends BasicDAO { + " ORDER BY " + REMOTE_PLAYLIST_DISPLAY_INDEX) Flowable> getPlaylists(); - @Upsert - long upsert(PlaylistRemoteEntity playlist); + @Query("SELECT " + REMOTE_PLAYLIST_ID + " FROM " + REMOTE_PLAYLIST_TABLE + + " WHERE " + REMOTE_PLAYLIST_URL + " = :url " + + "AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId") + Long getPlaylistIdInternal(long serviceId, String url); + + @Transaction + default long upsert(final PlaylistRemoteEntity playlist) { + final Long playlistId = getPlaylistIdInternal(playlist.getServiceId(), playlist.getUrl()); + + if (playlistId == null) { + return insert(playlist); + } else { + playlist.setUid(playlistId); + update(playlist); + return playlistId; + } + } @Query("DELETE FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " + REMOTE_PLAYLIST_ID + " = :playlistId") diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java index 7bb08de2a..c3996be1a 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java @@ -1,8 +1,13 @@ package org.schabi.newpipe.database.stream.dao; +import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID; +import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; + import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; import androidx.room.Query; -import androidx.room.Upsert; +import androidx.room.Transaction; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.stream.model.StreamStateEntity; @@ -11,9 +16,6 @@ import java.util.List; import io.reactivex.rxjava3.core.Flowable; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; - @Dao public interface StreamStateDAO extends BasicDAO { @Override @@ -35,6 +37,12 @@ public interface StreamStateDAO extends BasicDAO { @Query("DELETE FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId") int deleteState(long streamId); - @Upsert - long upsert(StreamStateEntity stream); + @Insert(onConflict = OnConflictStrategy.IGNORE) + void silentInsertInternal(StreamStateEntity streamState); + + @Transaction + default long upsert(final StreamStateEntity stream) { + silentInsertInternal(stream); + return update(stream); + } } From 72ce9e3c7504f890212793daee6f47204f07b39d Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 18:07:29 +0530 Subject: [PATCH 10/13] Remove duplicate update methods --- .../history/dao/SearchHistoryDAO.java | 24 ++++++++----------- .../database/playlist/dao/PlaylistDAO.java | 10 +++----- .../playlist/dao/PlaylistRemoteDAO.java | 4 ---- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java index 02d9fa555..880d8d53a 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java @@ -1,30 +1,26 @@ package org.schabi.newpipe.database.history.dao; -import androidx.annotation.Nullable; -import androidx.room.Dao; -import androidx.room.Query; -import androidx.room.Update; - -import org.schabi.newpipe.database.history.model.SearchHistoryEntry; - -import java.util.List; - -import io.reactivex.rxjava3.core.Flowable; - import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE; import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID; import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH; import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SERVICE_ID; import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME; +import androidx.annotation.Nullable; +import androidx.room.Dao; +import androidx.room.Query; + +import org.schabi.newpipe.database.history.model.SearchHistoryEntry; + +import java.util.List; + +import io.reactivex.rxjava3.core.Flowable; + @Dao public interface SearchHistoryDAO extends HistoryDAO { String ORDER_BY_CREATION_DATE = " ORDER BY " + CREATION_DATE + " DESC"; String ORDER_BY_MAX_CREATION_DATE = " ORDER BY MAX(" + CREATION_DATE + ") DESC"; - @Update - int update(SearchHistoryEntry historyEntry); - @Query("SELECT * FROM " + TABLE_NAME + " WHERE " + ID + " = (SELECT MAX(" + ID + ") FROM " + TABLE_NAME + ")") @Nullable diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java index 25401c160..7e943d28f 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java @@ -1,9 +1,11 @@ package org.schabi.newpipe.database.playlist.dao; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; + import androidx.room.Dao; import androidx.room.Query; import androidx.room.Transaction; -import androidx.room.Update; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistEntity; @@ -12,9 +14,6 @@ import java.util.List; import io.reactivex.rxjava3.core.Flowable; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; - @Dao public interface PlaylistDAO extends BasicDAO { @Override @@ -25,9 +24,6 @@ public interface PlaylistDAO extends BasicDAO { @Query("DELETE FROM " + PLAYLIST_TABLE) int deleteAll(); - @Update - int update(PlaylistEntity playlist); - @Override default Flowable> listByService(final int serviceId) { throw new UnsupportedOperationException(); diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java index 43ac47ca8..01ae08afa 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java @@ -9,7 +9,6 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.RE import androidx.room.Dao; import androidx.room.Query; import androidx.room.Transaction; -import androidx.room.Update; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; @@ -20,9 +19,6 @@ import io.reactivex.rxjava3.core.Flowable; @Dao public interface PlaylistRemoteDAO extends BasicDAO { - @Update - int update(PlaylistRemoteEntity entity); - @Override @Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE) Flowable> getAll(); From 99f22756e1b50ba1caec7067f120b05506718d8e Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 22 Jun 2024 18:32:03 +0530 Subject: [PATCH 11/13] Added Coil helper method --- .../feed/notifications/NotificationHelper.kt | 26 +++++-------------- .../SeekbarPreviewThumbnailHolder.java | 2 +- .../external_communication/ShareUtils.java | 2 +- .../schabi/newpipe/util/image/CoilHelper.kt | 20 +++++++++----- 4 files changed, 22 insertions(+), 28 deletions(-) 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 3cc13dbfc..768cecb67 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 @@ -14,15 +14,12 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.content.ContextCompat import androidx.core.content.getSystemService -import androidx.core.graphics.drawable.toBitmapOrNull import androidx.preference.PreferenceManager -import coil.imageLoader -import coil.request.ImageRequest 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.ImageStrategy +import org.schabi.newpipe.util.image.CoilHelper /** * Helper for everything related to show notifications about new streams to the user. @@ -67,24 +64,15 @@ 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) ) - val request = ImageRequest.Builder(context) - .data(data.avatarUrl?.takeIf { ImageStrategy.shouldLoadImages() }) - .placeholder(R.drawable.ic_newpipe_triangle_white) - .error(R.drawable.ic_newpipe_triangle_white) - .build() - val avatarIcon = context.imageLoader.execute(request).drawable?.toBitmapOrNull() + val avatarIcon = + CoilHelper.loadBitmap(context, data.avatarUrl, R.drawable.ic_newpipe_triangle_white) summaryBuilder.setLargeIcon(avatarIcon) 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 c7c53b097..d09664aeb 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 @@ -179,7 +179,7 @@ public class SeekbarPreviewThumbnailHolder { // Gets the bitmap within the timeout of 15 seconds imposed by default by OkHttpClient // Ensure that you are not running on the main thread, otherwise this will hang - final var bitmap = CoilHelper.INSTANCE.loadBitmap(App.getApp(), url); + final var bitmap = CoilHelper.INSTANCE.loadBitmapBlocking(App.getApp(), 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/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java index 3e93abf4d..087345af5 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 @@ -369,7 +369,7 @@ public final class ShareUtils { @NonNull final Context context, @NonNull final String thumbnailUrl) { try { - final var bitmap = CoilHelper.INSTANCE.loadBitmap(context, thumbnailUrl); + final var bitmap = CoilHelper.INSTANCE.loadBitmapBlocking(context, thumbnailUrl); if (bitmap == null) { 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 index 3977212bf..812cd1d7e 100644 --- a/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt @@ -6,12 +6,12 @@ import android.util.Log import android.widget.ImageView import androidx.annotation.DrawableRes import androidx.core.graphics.drawable.toBitmapOrNull -import coil.executeBlocking import coil.imageLoader import coil.request.ImageRequest import coil.size.Size import coil.target.Target import coil.transform.Transformation +import kotlinx.coroutines.runBlocking import org.schabi.newpipe.MainActivity import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Image @@ -19,13 +19,19 @@ import org.schabi.newpipe.ktx.scale import kotlin.math.min object CoilHelper { - private const val TAG = "CoilHelper" + private val TAG = CoilHelper::class.java.simpleName - fun loadBitmap(context: Context, url: String): Bitmap? { - val request = ImageRequest.Builder(context) - .data(url) - .build() - return context.imageLoader.executeBlocking(request).drawable?.toBitmapOrNull() + fun loadBitmapBlocking(context: Context, url: String?) = runBlocking { + loadBitmap(context, url, 0) + } + + suspend fun loadBitmap( + context: Context, + url: String?, + @DrawableRes placeholderResId: Int + ): Bitmap? { + val request = getImageRequest(context, url, placeholderResId).build() + return context.imageLoader.execute(request).drawable?.toBitmapOrNull() } fun loadAvatar(target: ImageView, images: List) { From dac292181d1153416d4f8306ce1a5335a93d4217 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 23 Jun 2024 11:21:08 +0530 Subject: [PATCH 12/13] Remove unused imports --- .../org/schabi/newpipe/local/feed/service/FeedLoadManager.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt index 59729ecf4..a4814621f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt @@ -11,9 +11,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flatMap import kotlinx.coroutines.flow.flatMapConcat -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.takeWhile From ace7923e2fa7916235a92b62e777c5fcaa800c13 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 23 Jun 2024 11:59:21 +0530 Subject: [PATCH 13/13] Avoid some potential issues with blocking calls in suspend functions --- .../org/schabi/newpipe/database/feed/dao/FeedDAO.kt | 3 --- .../newpipe/database/subscription/SubscriptionDAO.kt | 11 ++++++++--- .../newpipe/local/subscription/SubscriptionManager.kt | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index 32665ebcb..ec6e2b4e6 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -140,9 +140,6 @@ interface FeedDAO { ) suspend fun unlinkOldLivestreams(subscriptionId: Long) - @Insert(onConflict = OnConflictStrategy.IGNORE) - fun insert(feedEntity: FeedEntity) - @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertAll(entities: List): List diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt index 10f2dfc22..56ba2038e 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt @@ -6,12 +6,17 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.RewriteQueriesToDropUnusedColumns import androidx.room.Transaction +import androidx.room.Update import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe import org.schabi.newpipe.database.BasicDAO @Dao interface SubscriptionDAO : BasicDAO { + // TODO: Replace with the standard update method when migrating to suspend functions + @Update + suspend fun updateSubscription(entity: SubscriptionEntity) + @Query("SELECT COUNT(*) FROM subscriptions") fun rowCount(): Flowable @@ -84,10 +89,10 @@ interface SubscriptionDAO : BasicDAO { fun deleteSubscription(serviceId: Int, url: String): Int @Query("SELECT uid FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId") - fun getSubscriptionIdInternal(serviceId: Int, url: String): Long? + suspend fun getSubscriptionIdInternal(serviceId: Int, url: String): Long? @Insert(onConflict = OnConflictStrategy.IGNORE) - fun silentInsertAllInternal(entities: List): List + suspend fun silentInsertAllInternal(entities: List): List @Transaction suspend fun upsertAll(entities: List): List { @@ -103,7 +108,7 @@ interface SubscriptionDAO : BasicDAO { ?: throw IllegalStateException("Subscription cannot be null just after insertion.") entity.uid = subscriptionIdFromDb - update(entity) + updateSubscription(entity) } } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt index a206b8477..0af097f5b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt @@ -112,7 +112,7 @@ class SubscriptionManager(context: Context) { info.description?.let { subscriptionEntity.description = it } info.subscriberCount?.let { subscriptionEntity.subscriberCount = it } - subscriptionTable.update(subscriptionEntity) + subscriptionTable.updateSubscription(subscriptionEntity) } fun deleteSubscription(serviceId: Int, url: String): Completable { @@ -142,7 +142,7 @@ class SubscriptionManager(context: Context) { .map { channel -> channel.relatedItems.filterIsInstance().map { stream -> StreamEntity(stream) } } .flatMapCompletable { entities -> Completable.fromAction { - runBlocking { database.streamDAO().upsertAll(entities) } + database.streamDAO().upsertAll(entities) } }.onErrorComplete() }