diff options
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/services')
5 files changed, 245 insertions, 144 deletions
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 ededaad4..ee515a55 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package com.pitchedapps.frost.services import android.app.Notification @@ -26,8 +42,12 @@ import com.pitchedapps.frost.facebook.parsers.NotifParser import com.pitchedapps.frost.facebook.parsers.ParseNotification import com.pitchedapps.frost.glide.FrostGlide import com.pitchedapps.frost.glide.GlideApp -import com.pitchedapps.frost.utils.* -import java.util.* +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.frostEvent +import com.pitchedapps.frost.utils.isIndependent +import java.util.Locale /** * Created by Allan Wang on 2017-07-08. @@ -40,33 +60,38 @@ private val _40_DP = 40.dpToPx * Enum to handle notification creations */ enum class NotificationType( - private val channelId: String, - private val overlayContext: OverlayContext, - private val fbItem: FbItem, - private val parser: FrostParser<ParseNotification>, - private val getTime: (notif: NotificationModel) -> Long, - private val putTime: (notif: NotificationModel, time: Long) -> NotificationModel, - private val ringtone: () -> String) { + private val channelId: String, + private val overlayContext: OverlayContext, + private val fbItem: FbItem, + private val parser: FrostParser<ParseNotification>, + private val getTime: (notif: NotificationModel) -> Long, + private val putTime: (notif: NotificationModel, time: Long) -> NotificationModel, + private val ringtone: () -> String +) { - GENERAL(NOTIF_CHANNEL_GENERAL, - OverlayContext.NOTIFICATION, - FbItem.NOTIFICATIONS, - NotifParser, - NotificationModel::epoch, - { notif, time -> notif.copy(epoch = time) }, - Prefs::notificationRingtone) { + GENERAL( + NOTIF_CHANNEL_GENERAL, + OverlayContext.NOTIFICATION, + FbItem.NOTIFICATIONS, + NotifParser, + NotificationModel::epoch, + { notif, time -> notif.copy(epoch = time) }, + Prefs::notificationRingtone + ) { override fun bindRequest(content: NotificationContent, cookie: String) = - FrostRunnable.prepareMarkNotificationRead(content.id, cookie) + FrostRunnable.prepareMarkNotificationRead(content.id, cookie) }, - MESSAGE(NOTIF_CHANNEL_MESSAGES, - OverlayContext.MESSAGE, - FbItem.MESSAGES, - MessageParser, - NotificationModel::epochIm, - { notif, time -> notif.copy(epochIm = time) }, - Prefs::messageRingtone); + MESSAGE( + NOTIF_CHANNEL_MESSAGES, + OverlayContext.MESSAGE, + FbItem.MESSAGES, + MessageParser, + NotificationModel::epochIm, + { notif, time -> notif.copy(epochIm = time) }, + Prefs::messageRingtone + ); private val groupPrefix = "frost_${name.toLowerCase(Locale.CANADA)}" @@ -133,13 +158,15 @@ enum class NotificationType( } fun debugNotification(context: Context, data: CookieModel) { - val content = NotificationContent(data, - System.currentTimeMillis(), - "https://github.com/AllanWang/Frost-for-Facebook", - "Debug Notif", - "Test 123", - System.currentTimeMillis() / 1000, - "https://www.iconexperience.com/_img/v_collection_png/256x256/shadow/dog.png") + val content = NotificationContent( + data, + System.currentTimeMillis(), + "https://github.com/AllanWang/Frost-for-Facebook", + "Debug Notif", + "Test 123", + System.currentTimeMillis() / 1000, + "https://www.iconexperience.com/_img/v_collection_png/256x256/shadow/dog.png" + ) createNotification(context, content).notify(context) } @@ -147,44 +174,43 @@ enum class NotificationType( * Create and submit a new notification with the given [content] */ private fun createNotification(context: Context, content: NotificationContent): FrostNotification = - with(content) { - val intent = Intent(context, FrostWebActivity::class.java) - // TODO temp fix; we will show notification page for dependent urls. We can trigger a click next time - intent.data = Uri.parse(if (href.isIndependent) href else FbItem.NOTIFICATIONS.url) - intent.putExtra(ARG_USER_ID, data.id) - overlayContext.put(intent) - bindRequest(intent, content, data.cookie) + with(content) { + val intent = Intent(context, FrostWebActivity::class.java) + // TODO temp fix; we will show notification page for dependent urls. We can trigger a click next time + intent.data = Uri.parse(if (href.isIndependent) href else FbItem.NOTIFICATIONS.url) + intent.putExtra(ARG_USER_ID, data.id) + overlayContext.put(intent) + bindRequest(intent, content, data.cookie) - val group = "${groupPrefix}_${data.id}" - val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) - val notifBuilder = context.frostNotification(channelId) - .setContentTitle(title ?: context.string(R.string.frost_name)) - .setContentText(text) - .setContentIntent(pendingIntent) - .setCategory(Notification.CATEGORY_SOCIAL) - .setSubText(data.name) - .setGroup(group) + val group = "${groupPrefix}_${data.id}" + val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) + val notifBuilder = context.frostNotification(channelId) + .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 $content" } + if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000) + L.v { "Notif load $content" } - if (profileUrl != null) { - try { - val profileImg = GlideApp.with(context) - .asBitmap() - .load(profileUrl) - .transform(FrostGlide.circleCrop) - .submit(_40_DP, _40_DP) - .get() - notifBuilder.setLargeIcon(profileImg) - } catch (e: Exception) { - L.e { "Failed to get image $profileUrl" } - } + if (profileUrl != null) { + try { + val profileImg = GlideApp.with(context) + .asBitmap() + .load(profileUrl) + .transform(FrostGlide.circleCrop) + .submit(_40_DP, _40_DP) + .get() + notifBuilder.setLargeIcon(profileImg) + } catch (e: Exception) { + L.e { "Failed to get image $profileUrl" } } - - FrostNotification(group, notifId, notifBuilder) } + FrostNotification(group, notifId, notifBuilder) + } /** * Create a summary notification to wrap the previous ones @@ -198,12 +224,12 @@ enum class NotificationType( val group = "${groupPrefix}_$userId" val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) val notifBuilder = context.frostNotification(channelId) - .setContentTitle(context.string(R.string.frost_name)) - .setContentText("$count ${context.string(fbItem.titleId)}") - .setGroup(group) - .setGroupSummary(true) - .setContentIntent(pendingIntent) - .setCategory(Notification.CATEGORY_SOCIAL) + .setContentTitle(context.string(R.string.frost_name)) + .setContentText("$count ${context.string(fbItem.titleId)}") + .setGroup(group) + .setGroupSummary(true) + .setContentIntent(pendingIntent) + .setCategory(Notification.CATEGORY_SOCIAL) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notifBuilder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN) @@ -211,31 +237,33 @@ enum class NotificationType( return FrostNotification(group, 1, notifBuilder) } - } /** * Notification data holder */ -data class NotificationContent(val data: CookieModel, - val id: Long, - val href: String, - val title: String? = null, // defaults to frost title - val text: String, - val timestamp: Long, - val profileUrl: String?) { +data class NotificationContent( + val data: CookieModel, + val id: Long, + val href: String, + val title: String? = null, // defaults to frost title + val text: String, + val timestamp: Long, + val profileUrl: String? +) { val notifId = Math.abs(id.toInt()) - } /** * Wrapper for a complete notification builder and identifier * which can be immediately notified when given a [Context] */ -data class FrostNotification(private val tag: String, - private val id: Int, - val notif: NotificationCompat.Builder) { +data class FrostNotification( + private val tag: String, + private val id: Int, + val notif: NotificationCompat.Builder +) { fun withAlert(enable: Boolean, ringtone: String): FrostNotification { notif.setFrostAlert(enable, ringtone) @@ -243,15 +271,15 @@ data class FrostNotification(private val tag: String, } fun notify(context: Context) = - NotificationManagerCompat.from(context).notify(tag, id, notif.build()) + NotificationManagerCompat.from(context).notify(tag, id, notif.build()) } const val NOTIFICATION_PERIODIC_JOB = 7 fun Context.scheduleNotifications(minutes: Long): Boolean = - scheduleJob<NotificationService>(NOTIFICATION_PERIODIC_JOB, minutes) + scheduleJob<NotificationService>(NOTIFICATION_PERIODIC_JOB, minutes) const val NOTIFICATION_JOB_NOW = 6 fun Context.fetchNotifications(): Boolean = - fetchJob<NotificationService>(NOTIFICATION_JOB_NOW)
\ No newline at end of file + fetchJob<NotificationService>(NOTIFICATION_JOB_NOW) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt index b2ccaea2..22acc9fb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package com.pitchedapps.frost.services import android.app.job.JobInfo @@ -37,10 +53,10 @@ private enum class FrostRequestCommands : EnumBundle<FrostRequestCommands> { } override fun propagate(bundle: BaseBundle) = - FrostRunnable.prepareMarkNotificationRead( - bundle.getLong(ARG_0), - bundle.getCookie()) - + FrostRunnable.prepareMarkNotificationRead( + bundle.getLong(ARG_0), + bundle.getCookie() + ) }; override val bundleContract: EnumBundleCompanion<FrostRequestCommands> @@ -58,7 +74,6 @@ private enum class FrostRequestCommands : EnumBundle<FrostRequestCommands> { abstract fun propagate(bundle: BaseBundle): BaseBundle.() -> Unit companion object : EnumCompanion<FrostRequestCommands>("frost_arg_commands", values()) - } private const val ARG_COMMAND = "frost_request_command" @@ -99,8 +114,10 @@ object FrostRunnable { L.d { "Invalid notification id $id for marking as read" } return false } - return schedule(context, FrostRequestCommands.NOTIF_READ, - prepareMarkNotificationRead(id, cookie)) + return schedule( + context, FrostRequestCommands.NOTIF_READ, + prepareMarkNotificationRead(id, cookie) + ) } fun propagate(context: Context, intent: Intent?) { @@ -112,9 +129,11 @@ object FrostRunnable { schedule(context, command, builder) } - private fun schedule(context: Context, - command: FrostRequestCommands, - bundleBuilder: PersistableBundle.() -> Unit): Boolean { + private fun schedule( + context: Context, + command: FrostRequestCommands, + bundleBuilder: PersistableBundle.() -> Unit + ): Boolean { val scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val serviceComponent = ComponentName(context, FrostRequestService::class.java) val bundle = PersistableBundle() @@ -127,10 +146,10 @@ object FrostRunnable { } val builder = JobInfo.Builder(JOB_REQUEST_BASE + command.ordinal, serviceComponent) - .setMinimumLatency(0L) - .setExtras(bundle) - .setOverrideDeadline(2000L) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .setMinimumLatency(0L) + .setExtras(bundle) + .setOverrideDeadline(2000L) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) val result = scheduler.schedule(builder.build()) if (result <= 0) { L.eThrow("FrostRequestService scheduler failed for ${command.name}") @@ -139,7 +158,6 @@ object FrostRunnable { L.d { "Scheduled ${command.name}" } return true } - } class FrostRequestService : JobService() { @@ -183,4 +201,4 @@ class FrostRequestService : JobService() { } return true } -}
\ No newline at end of file +} 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 f15df482..4ede5163 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package com.pitchedapps.frost.services import android.app.job.JobParameters @@ -31,10 +47,12 @@ class NotificationService : JobService() { override fun onStopJob(params: JobParameters?): Boolean { val time = System.currentTimeMillis() - startTime L.d { "Notification service has finished abruptly in $time ms" } - frostEvent("NotificationTime", - "Type" to "Service force stop", - "IM Included" to Prefs.notificationsInstantMessages, - "Duration" to time) + frostEvent( + "NotificationTime", + "Type" to "Service force stop", + "IM Included" to Prefs.notificationsInstantMessages, + "Duration" to time + ) future?.cancel(true) future = null return false @@ -43,10 +61,12 @@ class NotificationService : JobService() { fun finish(params: JobParameters?) { val time = System.currentTimeMillis() - startTime L.i { "Notification service has finished in $time ms" } - frostEvent("NotificationTime", - "Type" to "Service", - "IM Included" to Prefs.notificationsInstantMessages, - "Duration" to time) + frostEvent( + "NotificationTime", + "Type" to "Service", + "IM Included" to Prefs.notificationsInstantMessages, + "Duration" to time + ) jobFinished(params, false) future?.cancel(true) future = null @@ -61,11 +81,13 @@ class NotificationService : JobService() { var notifCount = 0 cookies.forEach { val current = it.id == currentId - if (Prefs.notificationsGeneral - && (current || Prefs.notificationAllAccounts)) + if (Prefs.notificationsGeneral && + (current || Prefs.notificationAllAccounts) + ) notifCount += fetch(jobId, NotificationType.GENERAL, it) - if (Prefs.notificationsInstantMessages - && (current || Prefs.notificationsImAllAccounts)) + if (Prefs.notificationsInstantMessages && + (current || Prefs.notificationsImAllAccounts) + ) notifCount += fetch(jobId, NotificationType.MESSAGE, it) } @@ -99,10 +121,9 @@ class NotificationService : JobService() { private fun generalNotification(id: Int, textRes: Int, withDefaults: Boolean) { val notifBuilder = frostNotification(NOTIF_CHANNEL_GENERAL) - .setFrostAlert(withDefaults, Prefs.notificationRingtone) - .setContentTitle(string(R.string.frost_name)) - .setContentText(string(textRes)) + .setFrostAlert(withDefaults, Prefs.notificationRingtone) + .setContentTitle(string(R.string.frost_name)) + .setContentText(string(textRes)) NotificationManagerCompat.from(this).notify(id, notifBuilder.build()) } - -}
\ No newline at end of file +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt index 707220f6..20a497e3 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package com.pitchedapps.frost.services import android.app.Notification @@ -31,11 +47,11 @@ fun setupNotificationChannels(c: Context) { val appName = c.string(R.string.frost_name) val msg = c.string(R.string.messages) manager.notificationChannels - .filter { - it.id != NOTIF_CHANNEL_GENERAL - && it.id != NOTIF_CHANNEL_MESSAGES - } - .forEach { manager.deleteNotificationChannel(it.id) } + .filter { + it.id != NOTIF_CHANNEL_GENERAL && + it.id != NOTIF_CHANNEL_MESSAGES + } + .forEach { manager.deleteNotificationChannel(it.id) } manager.createNotificationChannel(NOTIF_CHANNEL_GENERAL, appName) manager.createNotificationChannel(NOTIF_CHANNEL_MESSAGES, "$appName: $msg") L.d { "Created notification channels: ${manager.notificationChannels.size} channels, ${manager.notificationChannelGroups.size} groups" } @@ -43,8 +59,10 @@ fun setupNotificationChannels(c: Context) { @RequiresApi(Build.VERSION_CODES.O) private fun NotificationManager.createNotificationChannel(id: String, name: String): NotificationChannel { - val channel = NotificationChannel(id, - name, NotificationManager.IMPORTANCE_DEFAULT) + val channel = NotificationChannel( + id, + name, NotificationManager.IMPORTANCE_DEFAULT + ) channel.enableLights(true) channel.lightColor = Prefs.accentColor channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC @@ -53,14 +71,14 @@ private fun NotificationManager.createNotificationChannel(id: String, name: Stri } fun Context.frostNotification(id: String) = - NotificationCompat.Builder(this, id) - .apply { - setSmallIcon(R.drawable.frost_f_24) - setAutoCancel(true) - setOnlyAlertOnce(true) - setStyle(NotificationCompat.BigTextStyle()) - color = color(R.color.frost_notification_accent) - } + NotificationCompat.Builder(this, id) + .apply { + setSmallIcon(R.drawable.frost_f_24) + setAutoCancel(true) + setOnlyAlertOnce(true) + setStyle(NotificationCompat.BigTextStyle()) + color = color(R.color.frost_notification_accent) + } /** * Dictates whether a notification should have sound/vibration/lights or not @@ -70,8 +88,9 @@ fun Context.frostNotification(id: String) = fun NotificationCompat.Builder.setFrostAlert(enable: Boolean, ringtone: String): NotificationCompat.Builder { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setGroupAlertBehavior( - if (enable) NotificationCompat.GROUP_ALERT_CHILDREN - else NotificationCompat.GROUP_ALERT_SUMMARY) + if (enable) NotificationCompat.GROUP_ALERT_CHILDREN + else NotificationCompat.GROUP_ALERT_SUMMARY + ) } else if (!enable) { setDefaults(0) } else { @@ -111,10 +130,10 @@ inline fun <reified T : JobService> Context.scheduleJob(id: Int, minutes: Long): if (minutes < 0L) return true val serviceComponent = ComponentName(this, T::class.java) val builder = JobInfo.Builder(id, serviceComponent) - .setPeriodic(minutes * 60000) - .setExtras(id) - .setPersisted(true) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //TODO add options + .setPeriodic(minutes * 60000) + .setExtras(id) + .setPersisted(true) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //TODO add options val result = scheduler.schedule(builder.build()) if (result <= 0) { L.eThrow("${T::class.java.simpleName} scheduler failed") @@ -130,10 +149,10 @@ inline fun <reified T : JobService> Context.fetchJob(id: Int): Boolean { val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val serviceComponent = ComponentName(this, T::class.java) val builder = JobInfo.Builder(id, serviceComponent) - .setMinimumLatency(0L) - .setExtras(id) - .setOverrideDeadline(2000L) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .setMinimumLatency(0L) + .setExtras(id) + .setOverrideDeadline(2000L) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) val result = scheduler.schedule(builder.build()) if (result <= 0) { L.eThrow("${T::class.java.simpleName} instant scheduler failed") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt index 59df9ed7..2d86f3b9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2018 Allan Wang + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package com.pitchedapps.frost.services import android.content.BroadcastReceiver @@ -18,5 +34,4 @@ class UpdateReceiver : BroadcastReceiver() { L.d { "Frost has updated" } context.scheduleNotifications(Prefs.notificationFreq) //Update notifications } - -}
\ No newline at end of file +} |