From ca3c05eea02d304ccfe80facfe0baa07459792cd Mon Sep 17 00:00:00 2001 From: Ravi Date: Thu, 20 May 2021 19:11:23 +1000 Subject: [PATCH 1/8] Setup for local push notifications Add WorkManager setup for handling local push notification like blogging reminders --- .../org/wordpress/android/WordPressDebug.java | 3 +- .../java/org/wordpress/android/WordPress.java | 6 +- .../android/push/NotificationPushIds.kt | 1 + .../wordpress/android/util/UploadWorker.kt | 7 +- .../android/workers/CreateSitePushHandler.kt | 20 ++++ .../wordpress/android/workers/LocalPush.kt | 31 ++++++ .../workers/LocalPushHandlerFactory.kt | 22 ++++ .../workers/LocalPushScheduleWorker.kt | 104 ++++++++++++++++++ .../android/workers/LocalPushScheduler.kt | 47 ++++++++ .../workers/WordPressWorkersFactory.kt | 18 +++ 10 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt diff --git a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java index 9832be90da66..c1f38dda642a 100644 --- a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java +++ b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java @@ -12,7 +12,6 @@ import org.wordpress.android.modules.DaggerAppComponentDebug; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.UploadWorker; public class WordPressDebug extends WordPress { @Override @@ -29,7 +28,7 @@ public void onCreate() { protected void initWorkManager() { Configuration config = (new Configuration.Builder()) .setMinimumLoggingLevel(Log.DEBUG) - .setWorkerFactory(new UploadWorker.Factory(mUploadStarter, mSiteStore)) + .setWorkerFactory(mWordPressWorkerFactory) .build(); WorkManager.initialize(this, config); } diff --git a/WordPress/src/main/java/org/wordpress/android/WordPress.java b/WordPress/src/main/java/org/wordpress/android/WordPress.java index 1fd334af888f..a3b05b01b166 100644 --- a/WordPress/src/main/java/org/wordpress/android/WordPress.java +++ b/WordPress/src/main/java/org/wordpress/android/WordPress.java @@ -110,7 +110,6 @@ import org.wordpress.android.util.QuickStartUtils; import org.wordpress.android.util.RateLimitedTask; import org.wordpress.android.util.SiteUtils; -import org.wordpress.android.util.UploadWorker; import org.wordpress.android.util.UploadWorkerKt; import org.wordpress.android.util.VolleyUtils; import org.wordpress.android.util.WPActivityUtils; @@ -119,6 +118,7 @@ import org.wordpress.android.util.config.AppConfig; import org.wordpress.android.util.image.ImageManager; import org.wordpress.android.widgets.AppRatingDialog; +import org.wordpress.android.workers.WordPressWorkersFactory; import java.io.File; import java.io.IOException; @@ -183,6 +183,7 @@ public class WordPress extends MultiDexApplication implements HasAndroidInjector @Inject AppConfig mAppConfig; @Inject ImageEditorFileUtils mImageEditorFileUtils; @Inject ExPlat mExPlat; + @Inject WordPressWorkersFactory mWordPressWorkerFactory; @Inject @Named(APPLICATION_SCOPE) CoroutineScope mAppScope; // For development and production `AnalyticsTrackerNosara`, for testing a mocked `Tracker` will be injected. @@ -370,9 +371,8 @@ public void onConnectionSuspended(int i) { } protected void initWorkManager() { - UploadWorker.Factory factory = new UploadWorker.Factory(mUploadStarter, mSiteStore); androidx.work.Configuration config = - (new androidx.work.Configuration.Builder()).setWorkerFactory(factory).build(); + (new androidx.work.Configuration.Builder()).setWorkerFactory(mWordPressWorkerFactory).build(); WorkManager.initialize(this, config); } diff --git a/WordPress/src/main/java/org/wordpress/android/push/NotificationPushIds.kt b/WordPress/src/main/java/org/wordpress/android/push/NotificationPushIds.kt index 72b81ed4bcd2..68960ba6792b 100644 --- a/WordPress/src/main/java/org/wordpress/android/push/NotificationPushIds.kt +++ b/WordPress/src/main/java/org/wordpress/android/push/NotificationPushIds.kt @@ -8,5 +8,6 @@ object NotificationPushIds { const val ACTIONS_PROGRESS_NOTIFICATION_ID = 50000 const val PENDING_DRAFTS_NOTIFICATION_ID = 600001 const val QUICK_START_REMINDER_NOTIFICATION_ID = 4001 + const val LOCAL_NOTIFICATION_ID = 70000 const val ZENDESK_PUSH_NOTIFICATION_ID = 1999999999 } diff --git a/WordPress/src/main/java/org/wordpress/android/util/UploadWorker.kt b/WordPress/src/main/java/org/wordpress/android/util/UploadWorker.kt index 6d0e3189f694..218ac1ba9d3c 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/UploadWorker.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/UploadWorker.kt @@ -52,8 +52,11 @@ class UploadWorker( workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { - // TODO This should use the [workerClassName] if there are other of Worker subclasses in the project - return UploadWorker(appContext, workerParameters, uploadStarter, siteStore) + return if (workerClassName == UploadWorker::class.java.name) { + UploadWorker(appContext, workerParameters, uploadStarter, siteStore) + } else { + null + } } } } diff --git a/WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt b/WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt new file mode 100644 index 000000000000..30ba4019963d --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt @@ -0,0 +1,20 @@ +package org.wordpress.android.workers + +import android.content.Context +import android.content.Intent +import org.wordpress.android.fluxc.store.AccountStore +import org.wordpress.android.fluxc.store.SiteStore +import javax.inject.Inject + +class CreateSitePushHandler @Inject constructor( + private val accountStore: AccountStore, + private val siteStore: SiteStore +) : LocalPushHandler { + override fun shouldShowNotification(): Boolean { + return accountStore.hasAccessToken() && !siteStore.hasSite() + } + + override fun buildIntent(context: Context): Intent { + return Intent() // TODO: Replace this with respective intent in ActivityLauncher + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt new file mode 100644 index 000000000000..1fdda64ad3a9 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt @@ -0,0 +1,31 @@ +package org.wordpress.android.workers + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.wordpress.android.push.NotificationPushIds +import java.util.concurrent.TimeUnit + +data class LocalPush( + val type: Type, + val delay: Long, + val delayUnits: TimeUnit, + @StringRes val title: Int, + @StringRes val text: Int, + @DrawableRes val icon: Int, + val uniqueId: Int? = null +) { + val id = uniqueId ?: NotificationPushIds.LOCAL_NOTIFICATION_ID + type.ordinal + + enum class Type(val tag: String) { + CREATE_SITE("create_site"); + + companion object { + fun fromTag(tag: String?): Type? { + return when (tag) { + CREATE_SITE.tag -> CREATE_SITE + else -> null + } + } + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt new file mode 100644 index 000000000000..7179b88265ad --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt @@ -0,0 +1,22 @@ +package org.wordpress.android.workers + +import android.content.Context +import android.content.Intent +import org.wordpress.android.workers.LocalPush.Type +import org.wordpress.android.workers.LocalPush.Type.CREATE_SITE +import javax.inject.Inject + +class LocalPushHandlerFactory @Inject constructor( + private val createSitePushHandler: CreateSitePushHandler +) { + fun buildLocalPushHandler(type: Type): LocalPushHandler { + return when (type) { + CREATE_SITE -> createSitePushHandler + } + } +} + +interface LocalPushHandler { + fun shouldShowNotification(): Boolean + fun buildIntent(context: Context): Intent +} diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt new file mode 100644 index 000000000000..b0c026653a46 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt @@ -0,0 +1,104 @@ +package org.wordpress.android.workers + +import android.app.PendingIntent +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toBitmap +import androidx.work.CoroutineWorker +import androidx.work.Data +import androidx.work.ListenableWorker +import androidx.work.WorkerFactory +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import org.wordpress.android.R +import org.wordpress.android.workers.LocalPush.Type + +class LocalPushScheduleWorker( + val context: Context, + params: WorkerParameters, + private val localPushHandlerFactory: LocalPushHandlerFactory +) : CoroutineWorker(context, params) { + override suspend fun doWork(): Result { + try { + val type = Type.fromTag(inputData.getString(TYPE)) ?: return Result.failure() + val handler = localPushHandlerFactory.buildLocalPushHandler(type) + if (!handler.shouldShowNotification()) { + return Result.failure() + } + val id = inputData.getInt(ID, -1) + val title = inputData.getInt(TITLE, -1) + val text = inputData.getInt(TEXT, -1) + val icon = inputData.getInt(ICON, -1) + if (id == -1 || title == -1 || text == -1) { + return Result.failure() + } + val pendingIntent = PendingIntent.getActivity( + context, + 0, + handler.buildIntent(context), + PendingIntent.FLAG_CANCEL_CURRENT + ) + val builder = NotificationCompat.Builder( + context, + context.getString(R.string.notification_channel_normal_id) + ) + .setContentIntent(pendingIntent) + .setSmallIcon(icon) + .setContentTitle(context.getString(title)) + .setContentText(context.getString(text)) + .addAction(R.drawable.ic_story_icon_24dp, context.getString(R.string.cancel), + pendingIntent) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setCategory(NotificationCompat.CATEGORY_REMINDER) + .setAutoCancel(true) + .setColorized(true) + .setColor(ContextCompat.getColor(context, R.color.blue_50)) + .setLargeIcon(ContextCompat.getDrawable(context, icon)?.toBitmap(100, 100)) + + with(NotificationManagerCompat.from(context)) { + notify(id, builder.build()) + } + + return Result.success() + } catch (e: Exception) { + return Result.failure() + } + } + + class Factory( + private val localPushHandlerFactory: LocalPushHandlerFactory + ) : WorkerFactory() { + override fun createWorker( + appContext: Context, + workerClassName: String, + workerParameters: WorkerParameters + ): ListenableWorker? { + return if (workerClassName == LocalPushScheduleWorker::class.java.name) { + LocalPushScheduleWorker(appContext, workerParameters, localPushHandlerFactory) + } else { + null + } + } + } + + companion object { + private const val TYPE = "key_type" + private const val ID = "key_id" + private const val TITLE = "key_title" + private const val TEXT = "key_text" + private const val ICON = "key_icon" + fun buildData(localNotification: LocalPush): Data { + return workDataOf( + TYPE to localNotification.type.tag, + ID to localNotification.id, + TITLE to localNotification.title, + TEXT to localNotification.text, + ICON to localNotification.icon + ) + } + } +} + + diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt new file mode 100644 index 000000000000..99242c01bb33 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt @@ -0,0 +1,47 @@ +package org.wordpress.android.workers + +import androidx.core.app.NotificationManagerCompat +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import org.wordpress.android.viewmodel.ContextProvider +import org.wordpress.android.workers.LocalPush.Type +import javax.inject.Inject + +class LocalPushScheduler @Inject constructor( + private val contextProvider: ContextProvider, + private val localPushHandlerFactory: LocalPushHandlerFactory +) { + fun scheduleOneTimeNotification(localNotification: LocalPush): Boolean { + val localPushHandler = localPushHandlerFactory.buildLocalPushHandler(localNotification.type) + if (localPushHandler.shouldShowNotification()) { + val work = OneTimeWorkRequestBuilder() + .setInitialDelay(localNotification.delay, localNotification.delayUnits) + .addTag(localNotification.type.tag) + .setInputData( + LocalPushScheduleWorker.buildData( + localNotification + ) + ) + .build() + + WorkManager.getInstance(contextProvider.getContext()).enqueue( + work + ) + return true + } else { + return false + } + } + + fun cancelScheduledNotification(notificationType: Type) { + WorkManager.getInstance(contextProvider.getContext()).cancelAllWorkByTag(notificationType.tag) + } + + fun cancelNotification(pushId: Int) { + if (pushId != -1) { + with(NotificationManagerCompat.from(contextProvider.getContext())) { + cancel(pushId) + } + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt new file mode 100644 index 000000000000..de49aa99aa87 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.workers + +import androidx.work.DelegatingWorkerFactory +import org.wordpress.android.fluxc.store.SiteStore +import org.wordpress.android.ui.uploads.UploadStarter +import org.wordpress.android.util.UploadWorker +import javax.inject.Inject + +class WordPressWorkersFactory @Inject constructor( + uploadStarter: UploadStarter, + siteStore: SiteStore, + localPushHandlerFactory: LocalPushHandlerFactory +) : DelegatingWorkerFactory() { + init { + addFactory(UploadWorker.Factory(uploadStarter, siteStore)) + addFactory(LocalPushScheduleWorker.Factory(localPushHandlerFactory)) + } +} From 787ea9486c836f76d746943d8ef69c93eb0ef1d4 Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 21 May 2021 13:25:22 +1000 Subject: [PATCH 2/8] Update LocalPushScheduleWorker.kt fix lint checks --- .../org/wordpress/android/workers/LocalPushScheduleWorker.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt index b0c026653a46..31b1e6b474e5 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt @@ -100,5 +100,3 @@ class LocalPushScheduleWorker( } } } - - From a6742bb9838a4c9c66d54f162168662e20130a8c Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 21 May 2021 17:17:25 +1000 Subject: [PATCH 3/8] Update LocalPushScheduleWorker.kt fix for detekt issues --- .../workers/LocalPushScheduleWorker.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt index 31b1e6b474e5..0cc2ddf58d39 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt @@ -20,24 +20,21 @@ class LocalPushScheduleWorker( params: WorkerParameters, private val localPushHandlerFactory: LocalPushHandlerFactory ) : CoroutineWorker(context, params) { + @Suppress("TooGenericExceptionCaught") override suspend fun doWork(): Result { try { - val type = Type.fromTag(inputData.getString(TYPE)) ?: return Result.failure() - val handler = localPushHandlerFactory.buildLocalPushHandler(type) - if (!handler.shouldShowNotification()) { - return Result.failure() - } + val type = Type.fromTag(inputData.getString(TYPE)) + val handler = type?.let { localPushHandlerFactory.buildLocalPushHandler(it) } + val id = inputData.getInt(ID, -1) val title = inputData.getInt(TITLE, -1) val text = inputData.getInt(TEXT, -1) val icon = inputData.getInt(ICON, -1) - if (id == -1 || title == -1 || text == -1) { - return Result.failure() - } + val pendingIntent = PendingIntent.getActivity( context, 0, - handler.buildIntent(context), + handler?.buildIntent(context), PendingIntent.FLAG_CANCEL_CURRENT ) val builder = NotificationCompat.Builder( @@ -55,7 +52,7 @@ class LocalPushScheduleWorker( .setAutoCancel(true) .setColorized(true) .setColor(ContextCompat.getColor(context, R.color.blue_50)) - .setLargeIcon(ContextCompat.getDrawable(context, icon)?.toBitmap(100, 100)) + .setLargeIcon(ContextCompat.getDrawable(context, icon)?.toBitmap(ICON_WIDTH, ICON_HEIGHT)) with(NotificationManagerCompat.from(context)) { notify(id, builder.build()) @@ -89,6 +86,8 @@ class LocalPushScheduleWorker( private const val TITLE = "key_title" private const val TEXT = "key_text" private const val ICON = "key_icon" + private const val ICON_WIDTH = 100 + private const val ICON_HEIGHT = 100 fun buildData(localNotification: LocalPush): Data { return workDataOf( TYPE to localNotification.type.tag, From 7692f63ff848339afef7e7febf9b58c980f8c090 Mon Sep 17 00:00:00 2001 From: Ravi Date: Mon, 24 May 2021 14:17:27 +1000 Subject: [PATCH 4/8] Rename to Push to Notification Rename and replace all instances of Push with Notification word for clarity --- ...er.kt => CreateSiteNotificationHandler.kt} | 4 ++-- .../{LocalPush.kt => LocalNotification.kt} | 2 +- .../LocalNotificationHandlerFactory.kt | 22 +++++++++++++++++++ ....kt => LocalNotificationScheduleWorker.kt} | 16 +++++++------- ...duler.kt => LocalNotificationScheduler.kt} | 14 ++++++------ .../workers/LocalPushHandlerFactory.kt | 22 ------------------- .../workers/WordPressWorkersFactory.kt | 4 ++-- 7 files changed, 42 insertions(+), 42 deletions(-) rename WordPress/src/main/java/org/wordpress/android/workers/{CreateSitePushHandler.kt => CreateSiteNotificationHandler.kt} (87%) rename WordPress/src/main/java/org/wordpress/android/workers/{LocalPush.kt => LocalNotification.kt} (96%) create mode 100644 WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt rename WordPress/src/main/java/org/wordpress/android/workers/{LocalPushScheduleWorker.kt => LocalNotificationScheduleWorker.kt} (84%) rename WordPress/src/main/java/org/wordpress/android/workers/{LocalPushScheduler.kt => LocalNotificationScheduler.kt} (69%) delete mode 100644 WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt diff --git a/WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt b/WordPress/src/main/java/org/wordpress/android/workers/CreateSiteNotificationHandler.kt similarity index 87% rename from WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt rename to WordPress/src/main/java/org/wordpress/android/workers/CreateSiteNotificationHandler.kt index 30ba4019963d..fca7dc38e5b0 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/CreateSitePushHandler.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/CreateSiteNotificationHandler.kt @@ -6,10 +6,10 @@ import org.wordpress.android.fluxc.store.AccountStore import org.wordpress.android.fluxc.store.SiteStore import javax.inject.Inject -class CreateSitePushHandler @Inject constructor( +class CreateSiteNotificationHandler @Inject constructor( private val accountStore: AccountStore, private val siteStore: SiteStore -) : LocalPushHandler { +) : LocalNotificationHandler { override fun shouldShowNotification(): Boolean { return accountStore.hasAccessToken() && !siteStore.hasSite() } diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt similarity index 96% rename from WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt rename to WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt index 1fdda64ad3a9..58380d3cbe0c 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalPush.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt @@ -5,7 +5,7 @@ import androidx.annotation.StringRes import org.wordpress.android.push.NotificationPushIds import java.util.concurrent.TimeUnit -data class LocalPush( +data class LocalNotification( val type: Type, val delay: Long, val delayUnits: TimeUnit, diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt new file mode 100644 index 000000000000..dc0cc6c788ec --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt @@ -0,0 +1,22 @@ +package org.wordpress.android.workers + +import android.content.Context +import android.content.Intent +import org.wordpress.android.workers.LocalNotification.Type +import org.wordpress.android.workers.LocalNotification.Type.CREATE_SITE +import javax.inject.Inject + +class LocalNotificationHandlerFactory @Inject constructor( + private val createSiteNotificationHandler: CreateSiteNotificationHandler +) { + fun buildLocalPushHandler(type: Type): LocalNotificationHandler { + return when (type) { + CREATE_SITE -> createSiteNotificationHandler + } + } +} + +interface LocalNotificationHandler { + fun shouldShowNotification(): Boolean + fun buildIntent(context: Context): Intent +} diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduleWorker.kt similarity index 84% rename from WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt rename to WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduleWorker.kt index 0cc2ddf58d39..cd6ce341868b 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduleWorker.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduleWorker.kt @@ -13,18 +13,18 @@ import androidx.work.WorkerFactory import androidx.work.WorkerParameters import androidx.work.workDataOf import org.wordpress.android.R -import org.wordpress.android.workers.LocalPush.Type +import org.wordpress.android.workers.LocalNotification.Type -class LocalPushScheduleWorker( +class LocalNotificationScheduleWorker( val context: Context, params: WorkerParameters, - private val localPushHandlerFactory: LocalPushHandlerFactory + private val localNotificationHandlerFactory: LocalNotificationHandlerFactory ) : CoroutineWorker(context, params) { @Suppress("TooGenericExceptionCaught") override suspend fun doWork(): Result { try { val type = Type.fromTag(inputData.getString(TYPE)) - val handler = type?.let { localPushHandlerFactory.buildLocalPushHandler(it) } + val handler = type?.let { localNotificationHandlerFactory.buildLocalPushHandler(it) } val id = inputData.getInt(ID, -1) val title = inputData.getInt(TITLE, -1) @@ -65,15 +65,15 @@ class LocalPushScheduleWorker( } class Factory( - private val localPushHandlerFactory: LocalPushHandlerFactory + private val localNotificationHandlerFactory: LocalNotificationHandlerFactory ) : WorkerFactory() { override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { - return if (workerClassName == LocalPushScheduleWorker::class.java.name) { - LocalPushScheduleWorker(appContext, workerParameters, localPushHandlerFactory) + return if (workerClassName == LocalNotificationScheduleWorker::class.java.name) { + LocalNotificationScheduleWorker(appContext, workerParameters, localNotificationHandlerFactory) } else { null } @@ -88,7 +88,7 @@ class LocalPushScheduleWorker( private const val ICON = "key_icon" private const val ICON_WIDTH = 100 private const val ICON_HEIGHT = 100 - fun buildData(localNotification: LocalPush): Data { + fun buildData(localNotification: LocalNotification): Data { return workDataOf( TYPE to localNotification.type.tag, ID to localNotification.id, diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt similarity index 69% rename from WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt rename to WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt index 99242c01bb33..da60a3a82561 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushScheduler.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt @@ -4,21 +4,21 @@ import androidx.core.app.NotificationManagerCompat import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import org.wordpress.android.viewmodel.ContextProvider -import org.wordpress.android.workers.LocalPush.Type +import org.wordpress.android.workers.LocalNotification.Type import javax.inject.Inject -class LocalPushScheduler @Inject constructor( +class LocalNotificationScheduler @Inject constructor( private val contextProvider: ContextProvider, - private val localPushHandlerFactory: LocalPushHandlerFactory + private val localNotificationHandlerFactory: LocalNotificationHandlerFactory ) { - fun scheduleOneTimeNotification(localNotification: LocalPush): Boolean { - val localPushHandler = localPushHandlerFactory.buildLocalPushHandler(localNotification.type) + fun scheduleOneTimeNotification(localNotification: LocalNotification): Boolean { + val localPushHandler = localNotificationHandlerFactory.buildLocalPushHandler(localNotification.type) if (localPushHandler.shouldShowNotification()) { - val work = OneTimeWorkRequestBuilder() + val work = OneTimeWorkRequestBuilder() .setInitialDelay(localNotification.delay, localNotification.delayUnits) .addTag(localNotification.type.tag) .setInputData( - LocalPushScheduleWorker.buildData( + LocalNotificationScheduleWorker.buildData( localNotification ) ) diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt deleted file mode 100644 index 7179b88265ad..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalPushHandlerFactory.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.wordpress.android.workers - -import android.content.Context -import android.content.Intent -import org.wordpress.android.workers.LocalPush.Type -import org.wordpress.android.workers.LocalPush.Type.CREATE_SITE -import javax.inject.Inject - -class LocalPushHandlerFactory @Inject constructor( - private val createSitePushHandler: CreateSitePushHandler -) { - fun buildLocalPushHandler(type: Type): LocalPushHandler { - return when (type) { - CREATE_SITE -> createSitePushHandler - } - } -} - -interface LocalPushHandler { - fun shouldShowNotification(): Boolean - fun buildIntent(context: Context): Intent -} diff --git a/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt index de49aa99aa87..f3a2abf86513 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt @@ -9,10 +9,10 @@ import javax.inject.Inject class WordPressWorkersFactory @Inject constructor( uploadStarter: UploadStarter, siteStore: SiteStore, - localPushHandlerFactory: LocalPushHandlerFactory + localNotificationHandlerFactory: LocalNotificationHandlerFactory ) : DelegatingWorkerFactory() { init { addFactory(UploadWorker.Factory(uploadStarter, siteStore)) - addFactory(LocalPushScheduleWorker.Factory(localPushHandlerFactory)) + addFactory(LocalNotificationScheduleWorker.Factory(localNotificationHandlerFactory)) } } From 420adefab4ef9d1e67aa8c43659ae5457ca2d087 Mon Sep 17 00:00:00 2001 From: Ravi Date: Mon, 24 May 2021 15:01:00 +1000 Subject: [PATCH 5/8] Replace Push with Notification Replaced some more instance of Push with Notification naming convention --- .../android/workers/LocalNotificationHandlerFactory.kt | 2 +- .../android/workers/LocalNotificationScheduler.kt | 6 +++--- ...cationScheduleWorker.kt => LocalNotificationWorker.kt} | 8 ++++---- .../wordpress/android/workers/WordPressWorkersFactory.kt | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename WordPress/src/main/java/org/wordpress/android/workers/{LocalNotificationScheduleWorker.kt => LocalNotificationWorker.kt} (93%) diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt index dc0cc6c788ec..d35b7727edee 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationHandlerFactory.kt @@ -9,7 +9,7 @@ import javax.inject.Inject class LocalNotificationHandlerFactory @Inject constructor( private val createSiteNotificationHandler: CreateSiteNotificationHandler ) { - fun buildLocalPushHandler(type: Type): LocalNotificationHandler { + fun buildLocalNotificationHandler(type: Type): LocalNotificationHandler { return when (type) { CREATE_SITE -> createSiteNotificationHandler } diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt index da60a3a82561..be21883b4d99 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduler.kt @@ -12,13 +12,13 @@ class LocalNotificationScheduler @Inject constructor( private val localNotificationHandlerFactory: LocalNotificationHandlerFactory ) { fun scheduleOneTimeNotification(localNotification: LocalNotification): Boolean { - val localPushHandler = localNotificationHandlerFactory.buildLocalPushHandler(localNotification.type) + val localPushHandler = localNotificationHandlerFactory.buildLocalNotificationHandler(localNotification.type) if (localPushHandler.shouldShowNotification()) { - val work = OneTimeWorkRequestBuilder() + val work = OneTimeWorkRequestBuilder() .setInitialDelay(localNotification.delay, localNotification.delayUnits) .addTag(localNotification.type.tag) .setInputData( - LocalNotificationScheduleWorker.buildData( + LocalNotificationWorker.buildData( localNotification ) ) diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduleWorker.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt similarity index 93% rename from WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduleWorker.kt rename to WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt index cd6ce341868b..83715c676c16 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationScheduleWorker.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt @@ -15,7 +15,7 @@ import androidx.work.workDataOf import org.wordpress.android.R import org.wordpress.android.workers.LocalNotification.Type -class LocalNotificationScheduleWorker( +class LocalNotificationWorker( val context: Context, params: WorkerParameters, private val localNotificationHandlerFactory: LocalNotificationHandlerFactory @@ -24,7 +24,7 @@ class LocalNotificationScheduleWorker( override suspend fun doWork(): Result { try { val type = Type.fromTag(inputData.getString(TYPE)) - val handler = type?.let { localNotificationHandlerFactory.buildLocalPushHandler(it) } + val handler = type?.let { localNotificationHandlerFactory.buildLocalNotificationHandler(it) } val id = inputData.getInt(ID, -1) val title = inputData.getInt(TITLE, -1) @@ -72,8 +72,8 @@ class LocalNotificationScheduleWorker( workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { - return if (workerClassName == LocalNotificationScheduleWorker::class.java.name) { - LocalNotificationScheduleWorker(appContext, workerParameters, localNotificationHandlerFactory) + return if (workerClassName == LocalNotificationWorker::class.java.name) { + LocalNotificationWorker(appContext, workerParameters, localNotificationHandlerFactory) } else { null } diff --git a/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt b/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt index f3a2abf86513..081bca7e2813 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/WordPressWorkersFactory.kt @@ -13,6 +13,6 @@ class WordPressWorkersFactory @Inject constructor( ) : DelegatingWorkerFactory() { init { addFactory(UploadWorker.Factory(uploadStarter, siteStore)) - addFactory(LocalNotificationScheduleWorker.Factory(localNotificationHandlerFactory)) + addFactory(LocalNotificationWorker.Factory(localNotificationHandlerFactory)) } } From 2b6598f96a4ed47787dc9a2c2b3eea05e8358248 Mon Sep 17 00:00:00 2001 From: Ravi Date: Wed, 26 May 2021 15:15:12 +1000 Subject: [PATCH 6/8] Refactor LocalNotficationWorker Removed unnecessary params like large icon and parameterised action icon and title --- .../android/workers/LocalNotification.kt | 2 + .../workers/LocalNotificationWorker.kt | 82 ++++++++++--------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt index 58380d3cbe0c..9095b58ffcd1 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotification.kt @@ -12,6 +12,8 @@ data class LocalNotification( @StringRes val title: Int, @StringRes val text: Int, @DrawableRes val icon: Int, + @DrawableRes val actionIcon: Int, + @StringRes val actionTitle: Int, val uniqueId: Int? = null ) { val id = uniqueId ?: NotificationPushIds.LOCAL_NOTIFICATION_ID + type.ordinal diff --git a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt index 83715c676c16..ff15b8641f87 100644 --- a/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt +++ b/WordPress/src/main/java/org/wordpress/android/workers/LocalNotificationWorker.kt @@ -5,7 +5,6 @@ import android.content.Context import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat -import androidx.core.graphics.drawable.toBitmap import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.ListenableWorker @@ -22,48 +21,50 @@ class LocalNotificationWorker( ) : CoroutineWorker(context, params) { @Suppress("TooGenericExceptionCaught") override suspend fun doWork(): Result { - try { - val type = Type.fromTag(inputData.getString(TYPE)) - val handler = type?.let { localNotificationHandlerFactory.buildLocalNotificationHandler(it) } + val id = inputData.getInt(ID, -1) - val id = inputData.getInt(ID, -1) - val title = inputData.getInt(TITLE, -1) - val text = inputData.getInt(TEXT, -1) - val icon = inputData.getInt(ICON, -1) + if (id == -1) return Result.failure() - val pendingIntent = PendingIntent.getActivity( - context, - 0, - handler?.buildIntent(context), - PendingIntent.FLAG_CANCEL_CURRENT - ) - val builder = NotificationCompat.Builder( - context, - context.getString(R.string.notification_channel_normal_id) - ) - .setContentIntent(pendingIntent) - .setSmallIcon(icon) - .setContentTitle(context.getString(title)) - .setContentText(context.getString(text)) - .addAction(R.drawable.ic_story_icon_24dp, context.getString(R.string.cancel), - pendingIntent) - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setCategory(NotificationCompat.CATEGORY_REMINDER) - .setAutoCancel(true) - .setColorized(true) - .setColor(ContextCompat.getColor(context, R.color.blue_50)) - .setLargeIcon(ContextCompat.getDrawable(context, icon)?.toBitmap(ICON_WIDTH, ICON_HEIGHT)) + with(NotificationManagerCompat.from(context)) { + notify(id, localNotificationBuilder().build()) + } - with(NotificationManagerCompat.from(context)) { - notify(id, builder.build()) - } + return Result.success() + } - return Result.success() - } catch (e: Exception) { - return Result.failure() + private fun localNotificationBuilder(): NotificationCompat.Builder { + val title = inputData.getInt(TITLE, -1) + val text = inputData.getInt(TEXT, -1) + val icon = inputData.getInt(ICON, -1) + val actionIcon = inputData.getInt(ACTION_ICON, -1) + val actionTitle = inputData.getInt(ACTION_TITLE, -1) + + return NotificationCompat.Builder(context, context.getString(R.string.notification_channel_normal_id)).apply { + val pendingIntent = getPendingIntent() + setContentIntent(pendingIntent) + setSmallIcon(icon) + setContentTitle(context.getString(title)) + setContentText(context.getString(text)) + addAction(actionIcon, context.getString(actionTitle), pendingIntent) + priority = NotificationCompat.PRIORITY_DEFAULT + setCategory(NotificationCompat.CATEGORY_REMINDER) + setAutoCancel(true) + setColorized(true) + color = ContextCompat.getColor(context, R.color.blue_50) } } + private fun getPendingIntent(): PendingIntent { + val type = Type.fromTag(inputData.getString(TYPE)) + val handler = type?.let { localNotificationHandlerFactory.buildLocalNotificationHandler(it) } + return PendingIntent.getActivity( + context, + 0, + handler?.buildIntent(context), + PendingIntent.FLAG_CANCEL_CURRENT + ) + } + class Factory( private val localNotificationHandlerFactory: LocalNotificationHandlerFactory ) : WorkerFactory() { @@ -86,15 +87,18 @@ class LocalNotificationWorker( private const val TITLE = "key_title" private const val TEXT = "key_text" private const val ICON = "key_icon" - private const val ICON_WIDTH = 100 - private const val ICON_HEIGHT = 100 + private const val ACTION_ICON = "key_action_icon" + private const val ACTION_TITLE = "key_action_title" + fun buildData(localNotification: LocalNotification): Data { return workDataOf( TYPE to localNotification.type.tag, ID to localNotification.id, TITLE to localNotification.title, TEXT to localNotification.text, - ICON to localNotification.icon + ICON to localNotification.icon, + ACTION_ICON to localNotification.actionIcon, + ACTION_TITLE to localNotification.actionTitle ) } } From 26eb8c740569052af2dc7f4ce913a596a3ef6270 Mon Sep 17 00:00:00 2001 From: Ravi Date: Wed, 26 May 2021 16:25:52 +1000 Subject: [PATCH 7/8] Create CreateSiteNotificationHandlerTest.kt add unit test --- .../CreateSiteNotificationHandlerTest.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/workers/CreateSiteNotificationHandlerTest.kt diff --git a/WordPress/src/test/java/org/wordpress/android/workers/CreateSiteNotificationHandlerTest.kt b/WordPress/src/test/java/org/wordpress/android/workers/CreateSiteNotificationHandlerTest.kt new file mode 100644 index 000000000000..d1abc35605e8 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/workers/CreateSiteNotificationHandlerTest.kt @@ -0,0 +1,40 @@ +package org.wordpress.android.workers + +import com.nhaarman.mockitokotlin2.whenever +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.wordpress.android.fluxc.store.AccountStore +import org.wordpress.android.fluxc.store.SiteStore + +@RunWith(MockitoJUnitRunner::class) +class CreateSiteNotificationHandlerTest { + @Mock + lateinit var accountStore: AccountStore + @Mock + lateinit var siteStore: SiteStore + + @Before + fun setUp() { + whenever(accountStore.hasAccessToken()).thenReturn(true) + whenever(siteStore.hasSite()).thenReturn(false) + } + + @Test + fun verifyShouldShowNotification() { + assertThat(accountStore.hasAccessToken() && !siteStore.hasSite()).isTrue + } + + @Test + fun verifyShouldNotShowNotification() { + assertThat(accountStore.hasAccessToken() && siteStore.hasSite()).isFalse + } + + @Test + fun verifyDoesNotShowNotification() { + assertThat(!accountStore.hasAccessToken() && !siteStore.hasSite()).isFalse + } +} From 1a6116afc9aaaceae950aa2e2bc1ec51081d182f Mon Sep 17 00:00:00 2001 From: Ravi Date: Wed, 26 May 2021 17:17:09 +1000 Subject: [PATCH 8/8] Create LocalNotificationHandlerFactoryTest.kt add unit test --- .../LocalNotificationHandlerFactoryTest.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/workers/LocalNotificationHandlerFactoryTest.kt diff --git a/WordPress/src/test/java/org/wordpress/android/workers/LocalNotificationHandlerFactoryTest.kt b/WordPress/src/test/java/org/wordpress/android/workers/LocalNotificationHandlerFactoryTest.kt new file mode 100644 index 000000000000..65cd0cb2f64d --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/workers/LocalNotificationHandlerFactoryTest.kt @@ -0,0 +1,28 @@ +package org.wordpress.android.workers + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.wordpress.android.workers.LocalNotification.Type.CREATE_SITE + +@RunWith(MockitoJUnitRunner::class) +class LocalNotificationHandlerFactoryTest { + @Mock + lateinit var createSiteNotificationHandler: CreateSiteNotificationHandler + + lateinit var localNotificationHandlerFactory: LocalNotificationHandlerFactory + + @Before + fun setUp() { + localNotificationHandlerFactory = LocalNotificationHandlerFactory(createSiteNotificationHandler) + } + + @Test + fun verifyLocalNotificationHandlerBuildsCorrectHandler() { + val handler = localNotificationHandlerFactory.buildLocalNotificationHandler(CREATE_SITE) + assertThat(handler).isInstanceOf(CreateSiteNotificationHandler::class.java) + } +}