From 8a246c72c8710c20749d5b944a0ce9ac1842c2e3 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sun, 17 Sep 2017 12:07:24 -0400 Subject: Disable deaults on creation --- .../pitchedapps/frost/facebook/FbUrlFormatter.kt | 2 +- .../frost/services/FrostNotifications.kt | 141 +++++++++++++-------- .../frost/services/NotificationService.kt | 50 ++------ .../com/pitchedapps/frost/web/FrostWebView.kt | 2 +- app/src/main/res/xml/frost_changelog.xml | 10 +- docs/Changelog.md | 1 + 6 files changed, 102 insertions(+), 104 deletions(-) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt index 3d016909..cd7d9002 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt @@ -30,7 +30,7 @@ class FbUrlFormatter(url: String) { cleanedUrl = cleanedUrl.substring(0, qm) } discardableQueries.forEach { queries.remove(it) } - if (cleanedUrl.startsWith("#!/")) cleanedUrl = cleanedUrl.substring(2) + if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2) if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1) cleanedUrl = cleanedUrl.replaceFirst(".facebook.com//", ".facebook.com/") //sometimes we are given a bad url L.v(null, "Formatted url from $url to $cleanedUrl") 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..2ab15fac 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 @@ -161,7 +190,7 @@ 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? ?: 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..adfed499 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.forEachIndexed unread@ { index, 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, index == 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.forEachIndexed unread@ { index, 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, index == 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 diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt index 7fb7324e..f6d64ab7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt @@ -70,7 +70,7 @@ class FrostWebView @JvmOverloads constructor( webChromeClient = FrostChromeClient(this) addJavascriptInterface(FrostJSI(this), "Frost") setBackgroundColor(Color.TRANSPARENT) - setDownloadListener { downloadUrl, _, _, _, _ -> context.frostDownload(downloadUrl) } + setDownloadListener(context::frostDownload) } } diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 42046133..19f328fa 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -11,6 +11,12 @@ + + + + + + @@ -19,10 +25,6 @@ - - - - diff --git a/docs/Changelog.md b/docs/Changelog.md index b9bf21ee..d7c017a9 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,7 @@ * Add contextual menu items. Easily go to your full list of notifications or messages from the overlay. * Ensure that bottom bar layout does not hide the web content * Add option to share external links to Frost +* Trigger notification service on each app start ## v1.4.13 * Prevent image loading from trimming too many characters -- cgit v1.2.3