Fix loading icon in streams notifications
This commit is contained in:
parent
19fd7bc37e
commit
01f3ed0e5e
4 changed files with 67 additions and 102 deletions
|
@ -4,7 +4,6 @@ import android.app.NotificationManager
|
|||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
|
@ -12,14 +11,11 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
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.PicassoHelper
|
||||
|
||||
/**
|
||||
* Helper for everything related to show notifications about new streams to the user.
|
||||
|
@ -34,7 +30,7 @@ class NotificationHelper(val context: Context) {
|
|||
* Show a notification about new streams from a single channel.
|
||||
* Opening the notification will open the corresponding channel page.
|
||||
*/
|
||||
fun displayNewStreamsNotification(data: FeedUpdateInfo): Completable {
|
||||
fun displayNewStreamsNotification(data: FeedUpdateInfo) {
|
||||
val newStreams: List<StreamInfoItem> = data.newStreams
|
||||
val summary = context.resources.getQuantityString(
|
||||
R.plurals.new_streams, newStreams.size, newStreams.size
|
||||
|
@ -59,12 +55,6 @@ class NotificationHelper(val context: Context) {
|
|||
.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setLargeIcon(
|
||||
BitmapFactory.decodeResource(
|
||||
context.resources,
|
||||
R.drawable.ic_newpipe_triangle_white
|
||||
)
|
||||
)
|
||||
.setColor(ContextCompat.getColor(context, R.color.ic_launcher_background))
|
||||
.setColorized(true)
|
||||
.setAutoCancel(true)
|
||||
|
@ -87,19 +77,17 @@ class NotificationHelper(val context: Context) {
|
|||
NavigationHelper
|
||||
.getChannelIntent(context, data.listInfo.serviceId, data.listInfo.url)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
else
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
return Single.create(NotificationIcon(context, data.avatarUrl))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { icon ->
|
||||
builder.setLargeIcon(icon)
|
||||
PicassoHelper.loadNotificationIcon(data.avatarUrl, context) { bitmap ->
|
||||
builder.setLargeIcon(bitmap)
|
||||
manager.notify(data.pseudoId, builder.build())
|
||||
}
|
||||
.ignoreElement()
|
||||
.onErrorComplete()
|
||||
.doOnComplete { manager.notify(data.pseudoId, builder.build()) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package org.schabi.newpipe.local.feed.notifications
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.squareup.picasso.Target
|
||||
import io.reactivex.rxjava3.core.SingleEmitter
|
||||
import io.reactivex.rxjava3.core.SingleOnSubscribe
|
||||
import org.schabi.newpipe.util.PicassoHelper
|
||||
|
||||
/**
|
||||
* Helper class to handle loading and resizing of icons
|
||||
* which are used going to be used in notifications.
|
||||
*/
|
||||
internal class NotificationIcon(
|
||||
context: Context,
|
||||
private val url: String,
|
||||
) : SingleOnSubscribe<Bitmap> {
|
||||
|
||||
private val size = getIconSize(context)
|
||||
|
||||
override fun subscribe(emitter: SingleEmitter<Bitmap>) {
|
||||
val target = SingleEmitterTarget(emitter)
|
||||
PicassoHelper.loadThumbnail(url)
|
||||
.resize(size, size)
|
||||
.centerCrop()
|
||||
.into(target)
|
||||
emitter.setCancellable {
|
||||
PicassoHelper.cancelRequest(target)
|
||||
}
|
||||
}
|
||||
|
||||
private class SingleEmitterTarget(private val emitter: SingleEmitter<Bitmap>) : Target {
|
||||
override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom?) {
|
||||
if (!emitter.isDisposed) {
|
||||
emitter.onSuccess(bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable?) {
|
||||
emitter.tryOnError(e)
|
||||
}
|
||||
|
||||
override fun onPrepareLoad(placeHolderDrawable: Drawable?) = Unit
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
fun getIconSize(context: Context): Int {
|
||||
val activityManager = context.getSystemService(
|
||||
Context.ACTIVITY_SERVICE
|
||||
) as ActivityManager?
|
||||
val size1 = activityManager?.launcherLargeIconSize ?: 0
|
||||
val size2 = context.resources.getDimensionPixelSize(android.R.dimen.app_icon_size)
|
||||
return maxOf(size2, size1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package org.schabi.newpipe.local.feed.notifications
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.Constraints
|
||||
|
@ -12,7 +13,7 @@ import androidx.work.PeriodicWorkRequest
|
|||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.rxjava3.RxWorker
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import org.schabi.newpipe.App
|
||||
import org.schabi.newpipe.R
|
||||
|
@ -34,30 +35,39 @@ class NotificationWorker(
|
|||
}
|
||||
private val feedLoadManager = FeedLoadManager(appContext)
|
||||
|
||||
override fun createWork(): Single<Result> = if (isEnabled(applicationContext)) {
|
||||
override fun createWork(): Single<Result> = if (areNotificationsEnabled(applicationContext)) {
|
||||
feedLoadManager.startLoading(
|
||||
ignoreOutdatedThreshold = true,
|
||||
groupId = FeedLoadManager.GROUP_NOTIFICATION_ENABLED
|
||||
)
|
||||
.doOnSubscribe { showLoadingFeedForegroundNotification() }
|
||||
.map { feed ->
|
||||
feed.mapNotNull { x ->
|
||||
x.value?.takeIf {
|
||||
it.newStreams.isNotEmpty()
|
||||
// filter out feedUpdateInfo items (i.e. channels) with nothing new
|
||||
feed.mapNotNull {
|
||||
it.value?.takeIf { feedUpdateInfo ->
|
||||
feedUpdateInfo.newStreams.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
.doOnSubscribe { setForegroundAsync(createForegroundInfo()) }
|
||||
.flatMapObservable { Observable.fromIterable(it) }
|
||||
.flatMapCompletable { x -> notificationHelper.displayNewStreamsNotification(x) }
|
||||
.toSingleDefault(Result.success())
|
||||
.observeOn(AndroidSchedulers.mainThread()) // Picasso requires calls from main thread
|
||||
.map { feedUpdateInfoList ->
|
||||
// display notifications for each feedUpdateInfo (i.e. channel)
|
||||
feedUpdateInfoList.forEach { feedUpdateInfo ->
|
||||
notificationHelper.displayNewStreamsNotification(feedUpdateInfo)
|
||||
}
|
||||
return@map Result.success()
|
||||
}
|
||||
.doOnError { throwable ->
|
||||
Log.e(TAG, "Error while displaying streams notifications", throwable)
|
||||
// TODO show error notification
|
||||
}
|
||||
.onErrorReturnItem(Result.failure())
|
||||
} else {
|
||||
// Can be the case when the user disables notifications for NewPipe
|
||||
// in the device's app settings.
|
||||
// the user can disable streams notifications in the device's app settings
|
||||
Single.just(Result.success())
|
||||
}
|
||||
|
||||
private fun createForegroundInfo(): ForegroundInfo {
|
||||
private fun showLoadingFeedForegroundNotification() {
|
||||
val notification = NotificationCompat.Builder(
|
||||
applicationContext,
|
||||
applicationContext.getString(R.string.notification_channel_id)
|
||||
|
@ -68,14 +78,15 @@ class NotificationWorker(
|
|||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setContentTitle(applicationContext.getString(R.string.feed_notification_loading))
|
||||
.build()
|
||||
return ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification)
|
||||
setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = App.PACKAGE_NAME + "_streams_notifications"
|
||||
private val TAG = NotificationWorker::class.java.simpleName
|
||||
private const val WORK_TAG = App.PACKAGE_NAME + "_streams_notifications"
|
||||
|
||||
private fun isEnabled(context: Context) =
|
||||
private fun areNotificationsEnabled(context: Context) =
|
||||
NotificationHelper.areNewStreamsNotificationsEnabled(context) &&
|
||||
NotificationHelper.areNotificationsEnabledOnDevice(context)
|
||||
|
||||
|
@ -86,7 +97,7 @@ class NotificationWorker(
|
|||
*/
|
||||
@JvmStatic
|
||||
fun initialize(context: Context) {
|
||||
if (isEnabled(context)) {
|
||||
if (areNotificationsEnabled(context)) {
|
||||
schedule(context)
|
||||
} else {
|
||||
cancel(context)
|
||||
|
@ -114,13 +125,13 @@ class NotificationWorker(
|
|||
options.interval,
|
||||
TimeUnit.MILLISECONDS
|
||||
).setConstraints(constraints)
|
||||
.addTag(TAG)
|
||||
.addTag(WORK_TAG)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context)
|
||||
.enqueueUniquePeriodicWork(
|
||||
TAG,
|
||||
WORK_TAG,
|
||||
if (force) {
|
||||
ExistingPeriodicWorkPolicy.REPLACE
|
||||
} else {
|
||||
|
@ -139,7 +150,7 @@ class NotificationWorker(
|
|||
@JvmStatic
|
||||
fun runNow(context: Context) {
|
||||
val request = OneTimeWorkRequestBuilder<NotificationWorker>()
|
||||
.addTag(TAG)
|
||||
.addTag(WORK_TAG)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
}
|
||||
|
@ -149,7 +160,7 @@ class NotificationWorker(
|
|||
*/
|
||||
@JvmStatic
|
||||
fun cancel(context: Context) {
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(WORK_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.squareup.picasso.Cache;
|
||||
import com.squareup.picasso.LruCache;
|
||||
|
@ -19,6 +21,7 @@ import org.schabi.newpipe.R;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
|
@ -161,6 +164,29 @@ public final class PicassoHelper {
|
|||
}
|
||||
|
||||
|
||||
public static void loadNotificationIcon(final String url,
|
||||
final Context context,
|
||||
final Consumer<Bitmap> bitmapConsumer) {
|
||||
loadImageDefault(url, R.drawable.ic_newpipe_triangle_white)
|
||||
.into(new Target() {
|
||||
@Override
|
||||
public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) {
|
||||
bitmapConsumer.accept(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapFailed(final Exception e, final Drawable errorDrawable) {
|
||||
bitmapConsumer.accept(BitmapFactory.decodeResource(context.getResources(),
|
||||
R.drawable.ic_newpipe_triangle_white));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareLoad(final Drawable placeHolderDrawable) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static RequestCreator loadImageDefault(final String url, final int placeholderResId) {
|
||||
if (!shouldLoadImages || isBlank(url)) {
|
||||
return picassoInstance
|
||||
|
|
Loading…
Reference in a new issue