diff options
author | Allan Wang <me@allanwang.ca> | 2017-09-18 10:43:55 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-18 10:43:55 -0400 |
commit | d75ad7fb42aba81b334f9453c012f04c3d5f3e0a (patch) | |
tree | 9dfd08dd48325ae7f813a4cfa43ec5629662db79 /app/src/main/kotlin/com/pitchedapps/frost/services | |
parent | 5ae7fd03522dd2c2843ff522314ab1d20f85d391 (diff) | |
download | frost-d75ad7fb42aba81b334f9453c012f04c3d5f3e0a.tar.gz frost-d75ad7fb42aba81b334f9453c012f04c3d5f3e0a.tar.bz2 frost-d75ad7fb42aba81b334f9453c012f04c3d5f3e0a.zip |
Fix/notification defaults (#308)
* Update downloader
* Disable deaults on creation
* Use notifCount rather than index
* Remove quiet
* Add checks to ensure job service exists
* Update changelog
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/services')
3 files changed, 105 insertions, 100 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt index ee0c2027..986467b8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt @@ -28,6 +28,8 @@ import java.io.File * * Background file downloader * All we are given is a link and a mime type + * + * With reference to the <a href="https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java">OkHttp3 sample</a> */ class DownloadService : IntentService("FrostVideoDownloader") { @@ -64,7 +66,7 @@ class DownloadService : IntentService("FrostVideoDownloader") { .url(url) .build() - notifBuilder = frostNotification.quiet + notifBuilder = frostNotification notifId = Math.abs(url.hashCode() + System.currentTimeMillis().toInt()) val cancelIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt index 800fe492..b892af91 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -22,6 +22,7 @@ import com.pitchedapps.frost.R import com.pitchedapps.frost.activities.FrostWebActivity import com.pitchedapps.frost.dbflow.CookieModel import com.pitchedapps.frost.enums.OverlayContext +import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.utils.* import org.jetbrains.anko.runOnUiThread @@ -34,16 +35,13 @@ import org.jetbrains.anko.runOnUiThread val Context.frostNotification: NotificationCompat.Builder - get() = frostNotification() + get() = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID).apply { + setSmallIcon(R.drawable.frost_f_24) + setAutoCancel(true) + color = color(R.color.frost_notification_accent) + } -/** - * Wrap the default builder with our icon and accent color - */ -fun Context.frostNotification(ringtone: String = Prefs.notificationRingtone): NotificationCompat.Builder - = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID).apply { - setSmallIcon(R.drawable.frost_f_24) - setAutoCancel(true) - color = color(R.color.frost_notification_accent) +fun NotificationCompat.Builder.withDefaults(ringtone: String = Prefs.notificationRingtone) = apply { var defaults = 0 if (Prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE if (Prefs.notificationSound) { @@ -54,12 +52,6 @@ fun Context.frostNotification(ringtone: String = Prefs.notificationRingtone): No setDefaults(defaults) } -val NotificationCompat.Builder.quiet - get() = apply { setDefaults(0) } - -val NotificationCompat.Builder.messageRingtone - get() = apply { } - val NotificationCompat.Builder.withBigText: NotificationCompat.BigTextStyle get() = NotificationCompat.BigTextStyle(this) @@ -81,8 +73,82 @@ class FrostNotificationTarget(val context: Context, } } -internal const val FROST_NOTIFICATION_GROUP = "frost" -internal const val FROST_MESSAGE_NOTIFICATION_GROUP = "frost_im" +/** + * Enum to handle notification creations + */ +enum class NotificationType( + private val groupPrefix: String, + private val overlayContext: OverlayContext, + private val contentRes: Int, + private val pendingUrl: String, + private val ringtone: () -> String) { + GENERAL("frost", OverlayContext.NOTIFICATION, R.string.notifications, FbItem.NOTIFICATIONS.url, { Prefs.notificationRingtone }), + MESSAGE("frost_im", OverlayContext.MESSAGE, R.string.messages, FbItem.MESSAGES.url, { Prefs.messageRingtone }); + + /** + * Create and submit a new notification with the given [content] + * If [withDefaults] is set, it will also add the appropriate sound, vibration, and light + * Note that when we have multiple notifications coming in at once, we don't want to have defaults for all of them + */ + fun createNotification(context: Context, content: NotificationContent, withDefaults: Boolean) { + with(content) { + val intent = Intent(context, FrostWebActivity::class.java) + intent.data = Uri.parse(href.formattedFbUrl) + intent.putExtra(ARG_USER_ID, data.id) + intent.putExtra(ARG_OVERLAY_CONTEXT, overlayContext) + val group = "${groupPrefix}_${data.id}" + val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) + val notifBuilder = context.frostNotification + .setContentTitle(title ?: context.string(R.string.frost_name)) + .setContentText(text) + .setContentIntent(pendingIntent) + .setCategory(Notification.CATEGORY_SOCIAL) + .setSubText(data.name) + .setGroup(group) + + if (withDefaults) + notifBuilder.withDefaults(ringtone()) + + if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000) + L.v("Notif load", context.toString()) + NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.withBigText.build()) + + if (profileUrl.isNotBlank()) { + context.runOnUiThread { + //todo verify if context is valid? + Glide.with(context) + .asBitmap() + .load(profileUrl) + .withRoundIcon() + .into(FrostNotificationTarget(context, notifId, group, notifBuilder)) + } + } + } + } + + /** + * Create a summary notification to wrap the previous ones + * This will always produce sound, vibration, and lights based on preferences + * and will only show if we have at least 2 notifications + */ + fun summaryNotification(context: Context, userId: Long, count: Int) { + frostAnswersCustom("Notifications", "Type" to name, "Count" to count) + if (count <= 1) return + val intent = Intent(context, FrostWebActivity::class.java) + intent.data = Uri.parse(pendingUrl) + intent.putExtra(ARG_USER_ID, userId) + val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) + val notifBuilder = context.frostNotification.withDefaults(ringtone()) + .setContentTitle(context.string(R.string.frost_name)) + .setContentText("$count ${context.string(contentRes)}") + .setGroup("${groupPrefix}_$userId") + .setGroupSummary(true) + .setContentIntent(pendingIntent) + .setCategory(Notification.CATEGORY_SOCIAL) + + NotificationManagerCompat.from(context).notify("${groupPrefix}_$userId", userId.toInt(), notifBuilder.build()) + } +} /** * Notification data holder @@ -93,44 +159,7 @@ data class NotificationContent(val data: CookieModel, val title: String? = null, val text: String, val timestamp: Long, - val profileUrl: String) { - - fun createNotification(context: Context) = createNotification(context, FROST_NOTIFICATION_GROUP) - - fun createMessageNotification(context: Context) = createNotification(context, FROST_MESSAGE_NOTIFICATION_GROUP) - - private fun createNotification(context: Context, groupPrefix: String) { - val intent = Intent(context, FrostWebActivity::class.java) - intent.data = Uri.parse(href.formattedFbUrl) - intent.putExtra(ARG_USER_ID, data.id) - val overlayContext = if (groupPrefix == FROST_MESSAGE_NOTIFICATION_GROUP) OverlayContext.MESSAGE else OverlayContext.NOTIFICATION - intent.putExtra(ARG_OVERLAY_CONTEXT, overlayContext) - val group = "${groupPrefix}_${data.id}" - val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) - val ringtone = if (groupPrefix == FROST_MESSAGE_NOTIFICATION_GROUP) Prefs.messageRingtone else Prefs.notificationRingtone - val notifBuilder = context.frostNotification(ringtone) - .setContentTitle(title ?: context.string(R.string.frost_name)) - .setContentText(text) - .setContentIntent(pendingIntent) - .setCategory(Notification.CATEGORY_SOCIAL) - .setSubText(data.name) - .setGroup(group) - - if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000) - L.v("Notif load", this.toString()) - NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.withBigText.build()) - - if (profileUrl.isNotBlank()) { - context.runOnUiThread { - Glide.with(context) - .asBitmap() - .load(profileUrl) - .withRoundIcon() - .into(FrostNotificationTarget(context, notifId, group, notifBuilder)) - } - } - } -} + val profileUrl: String) const val NOTIFICATION_PERIODIC_JOB = 7 @@ -139,7 +168,11 @@ const val NOTIFICATION_PERIODIC_JOB = 7 * returns false if an error occurs; true otherwise */ fun Context.scheduleNotifications(minutes: Long): Boolean { - val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler + val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler? + if (scheduler == null) { + L.e("JobScheduler not found; cannot schedule notifications") + return false + } scheduler.cancel(NOTIFICATION_PERIODIC_JOB) if (minutes < 0L) return true val serviceComponent = ComponentName(this, NotificationService::class.java) @@ -161,7 +194,11 @@ const val NOTIFICATION_JOB_NOW = 6 * Run notification job right now */ fun Context.fetchNotifications(): Boolean { - val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler + val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler? + if (scheduler == null) { + L.e("JobScheduler not found") + return false + } val serviceComponent = ComponentName(this, NotificationService::class.java) val builder = JobInfo.Builder(NOTIFICATION_JOB_NOW, serviceComponent) .setMinimumLatency(0L) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt index 9321c42f..d481e941 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt @@ -1,17 +1,12 @@ package com.pitchedapps.frost.services -import android.app.Notification -import android.app.PendingIntent import android.app.job.JobParameters import android.app.job.JobService import android.content.Context -import android.content.Intent -import android.net.Uri import android.support.v4.app.NotificationManagerCompat import ca.allanwang.kau.utils.string import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.R -import com.pitchedapps.frost.activities.FrostWebActivity import com.pitchedapps.frost.dbflow.CookieModel import com.pitchedapps.frost.dbflow.lastNotificationTime import com.pitchedapps.frost.dbflow.loadFbCookie @@ -21,7 +16,6 @@ import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.injectors.JsAssets -import com.pitchedapps.frost.utils.ARG_USER_ID import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.frostAnswersCustom @@ -130,20 +124,18 @@ class NotificationService : JobService() { val prevLatestEpoch = prevNotifTime.epoch L.v("Notif Prev Latest Epoch $prevLatestEpoch") var newLatestEpoch = prevLatestEpoch - unreadNotifications.forEach unread@ { - elem -> + unreadNotifications.forEach unread@ { elem -> val notif = parseNotification(data, elem) ?: return@unread L.v("Notif timestamp ${notif.timestamp}") if (notif.timestamp <= prevLatestEpoch) return@unread - notif.createNotification(this@NotificationService) + NotificationType.GENERAL.createNotification(this, notif, notifCount == 0) if (notif.timestamp > newLatestEpoch) newLatestEpoch = notif.timestamp notifCount++ } if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epoch = newLatestEpoch).save() L.d("Notif new latest epoch ${lastNotificationTime(data.id).epoch}") - frostAnswersCustom("Notifications", "Type" to "General", "Count" to notifCount) - summaryNotification(data.id, notifCount) + NotificationType.GENERAL.summaryNotification(this, data.id, notifCount) } fun parseNotification(data: CookieModel, element: Element): NotificationContent? { @@ -161,9 +153,6 @@ class NotificationService : JobService() { return NotificationContent(data, notifId.toInt(), a.attr("href"), null, text, epoch, pUrl) } - fun summaryNotification(userId: Long, count: Int) - = summaryNotification(userId, count, R.string.notifications, FbItem.NOTIFICATIONS.url, FROST_NOTIFICATION_GROUP) - /* * ---------------------------------------------------------------- * Instant message notification logic. @@ -174,8 +163,7 @@ class NotificationService : JobService() { inline fun fetchMessageNotifications(data: CookieModel, crossinline callback: (success: Boolean) -> Unit) { launchHeadlessHtmlExtractor(FbItem.MESSAGES.url, JsAssets.NOTIF_MSG) { - it.observeOn(Schedulers.newThread()).subscribe { - (html, errorRes) -> + it.observeOn(Schedulers.newThread()).subscribe { (html, errorRes) -> L.d("Notf IM html received") if (errorRes != -1) return@subscribe callback(false) fetchMessageNotifications(data, html) @@ -193,20 +181,18 @@ class NotificationService : JobService() { val prevLatestEpoch = prevNotifTime.epochIm L.v("Notif Prev Latest Im Epoch $prevLatestEpoch") var newLatestEpoch = prevLatestEpoch - unreadNotifications.forEach unread@ { - elem -> + unreadNotifications.forEach unread@ { elem -> val notif = parseMessageNotification(data, elem) ?: return@unread L.v("Notif im timestamp ${notif.timestamp}") if (notif.timestamp <= prevLatestEpoch) return@unread - notif.createMessageNotification(this@NotificationService) + NotificationType.MESSAGE.createNotification(this, notif, notifCount == 0) if (notif.timestamp > newLatestEpoch) newLatestEpoch = notif.timestamp notifCount++ } if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epochIm = newLatestEpoch).save() L.d("Notif new latest im epoch ${lastNotificationTime(data.id).epochIm}") - frostAnswersCustom("Notifications", "Type" to "Message", "Count" to notifCount) - summaryMessageNotification(data.id, notifCount) + NotificationType.MESSAGE.summaryNotification(this, data.id, notifCount) } fun parseMessageNotification(data: CookieModel, element: Element): NotificationContent? { @@ -225,32 +211,12 @@ class NotificationService : JobService() { return NotificationContent(data, notifId.toInt(), a.attr("href"), a.text(), text, epoch, pUrl) } - fun summaryMessageNotification(userId: Long, count: Int) - = summaryNotification(userId, count, R.string.messages, FbItem.MESSAGES.url, FROST_MESSAGE_NOTIFICATION_GROUP) - private fun Context.debugNotification(text: String) { if (!BuildConfig.DEBUG) return - val notifBuilder = frostNotification + val notifBuilder = frostNotification.withDefaults() .setContentTitle(string(R.string.frost_name)) .setContentText(text) NotificationManagerCompat.from(this).notify(999, notifBuilder.build()) } - private fun summaryNotification(userId: Long, count: Int, contentRes: Int, pendingUrl: String, groupPrefix: String) { - if (count <= 1) return - val intent = Intent(this, FrostWebActivity::class.java) - intent.data = Uri.parse(pendingUrl) - intent.putExtra(ARG_USER_ID, userId) - val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0) - val notifBuilder = frostNotification - .setContentTitle(string(R.string.frost_name)) - .setContentText("$count ${string(contentRes)}") - .setGroup("${groupPrefix}_$userId") - .setGroupSummary(true) - .setContentIntent(pendingIntent) - .setCategory(Notification.CATEGORY_SOCIAL) - - NotificationManagerCompat.from(this).notify("${groupPrefix}_$userId", userId.toInt(), notifBuilder.build()) - } - }
\ No newline at end of file |