aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-08 19:21:13 -0400
committerGitHub <noreply@github.com>2017-07-08 19:21:13 -0400
commit085295d5dbda84ff02221cc65bd472fff69e636e (patch)
tree12768bed871581a054941b030f1e1224278fc794 /app
parentb10a745c7f0f46f4f014e1ba7fa71172d7442b83 (diff)
downloadfrost-085295d5dbda84ff02221cc65bd472fff69e636e.tar.gz
frost-085295d5dbda84ff02221cc65bd472fff69e636e.tar.bz2
frost-085295d5dbda84ff02221cc65bd472fff69e636e.zip
Dev 1.2.1 (#43) - Revamp notificationsv1.2.1
* Test proguard * Test proguard without enums * Allow notifications from only current account * Prettify notifications * Clean up layouts * Test proguard log * Update rxkotlin * Update remaining dependencies
Diffstat (limited to 'app')
-rw-r--r--app/proguard-rules.pro19
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt10
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt11
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt149
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt117
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt18
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt40
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt4
-rw-r--r--app/src/main/res/values/strings_preferences6
-rw-r--r--app/src/main/res/xml/changelog.xml15
13 files changed, 269 insertions, 127 deletions
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 6df89e1c..9df5ace7 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -3,11 +3,11 @@
-keep class * extends com.raizlabs.android.dbflow.config.DatabaseHolder { *; }
-keepattributes *Annotation*
# Enums
--keepclassmembers class * extends java.lang.Enum {
- public *;
- public static **[] values();
- public static ** valueOf(java.lang.String);
-}
+#-keepclassmembers class * extends java.lang.Enum {
+# public *;
+# public static **[] values();
+# public static ** valueOf(java.lang.String);
+#}
# Crashlytics
-keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception
@@ -23,9 +23,12 @@
# IAB
-keep class com.android.vending.billing.**
# About libs
--keep class .R
--keep class **.R$* {
- <fields>;
+#-keep class .R
+#-keep class **.R$* {
+# <fields>;
+#}
+-keepclasseswithmembers class **.R$* {
+ public static final int define_*;
}
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
index 85a49730..4b62d244 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
@@ -4,7 +4,6 @@ import android.app.Application
import android.graphics.drawable.Drawable
import android.net.Uri
import android.widget.ImageView
-import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.signature.ApplicationVersionSignature
import com.crashlytics.android.Crashlytics
@@ -20,7 +19,6 @@ import com.raizlabs.android.dbflow.config.FlowConfig
import com.raizlabs.android.dbflow.config.FlowManager
import io.fabric.sdk.android.Fabric
import timber.log.Timber
-import timber.log.Timber.DebugTree
import java.util.*
@@ -42,7 +40,7 @@ class FrostApp : Application() {
// if (LeakCanary.isInAnalyzerProcess(this)) return
// refWatcher = LeakCanary.install(this)
if (BuildConfig.DEBUG) {
- Timber.plant(DebugTree())
+ Timber.plant(Timber.DebugTree())
// LeakCanary.enableDisplayLeakActivity(this)
} else {
Fabric.with(this, Crashlytics(), Answers())
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt
index 9b79b996..a27a1ee2 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt
@@ -13,19 +13,14 @@ import ca.allanwang.kau.utils.fadeOut
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.crashlytics.android.answers.LoginEvent
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.dbflow.fetchUsername
import com.pitchedapps.frost.dbflow.loadFbCookiesAsync
import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL
-import com.pitchedapps.frost.utils.L
-import com.pitchedapps.frost.utils.frostAnswers
-import com.pitchedapps.frost.utils.launchNewTask
-import com.pitchedapps.frost.utils.setFrostColors
+import com.pitchedapps.frost.utils.*
import com.pitchedapps.frost.web.LoginWebView
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
@@ -110,8 +105,7 @@ class LoginActivity : BaseActivity() {
fun loadProfile(id: Long) {
- val options = RequestOptions().transform(CircleCrop())
- Glide.with(this@LoginActivity).load(PROFILE_PICTURE_URL(id)).apply(options).listener(object : RequestListener<Drawable> {
+ Glide.with(this@LoginActivity).load(PROFILE_PICTURE_URL(id)).withRoundIcon().listener(object : RequestListener<Drawable> {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
profileObservable.onSuccess(true)
return false
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt
index f9a597db..ec7bb838 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt
@@ -49,6 +49,7 @@ import com.pitchedapps.frost.facebook.FbCookie.switchUser
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL
import com.pitchedapps.frost.fragments.WebFragment
+import com.pitchedapps.frost.services.NotificationContent
import com.pitchedapps.frost.utils.*
import com.pitchedapps.frost.utils.iab.validatePro
import com.pitchedapps.frost.views.BadgedIcon
@@ -58,6 +59,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
+import org.jetbrains.anko.doAsync
import org.jsoup.Jsoup
import java.util.concurrent.TimeUnit
@@ -145,6 +147,15 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract,
}
setFrostColors(toolbar, themeWindow = false, headers = arrayOf(tabs, appBar), backgrounds = arrayOf(viewPager))
validatePro()
+
+ doAsync {
+ val debugIcon = "https://scontent-sea1-1.xx.fbcdn.net/v/t1.0-1/cp0/e15/q65/p120x120/12994387_243040309382307_4586627375882013710_n.jpg?efg=eyJpIjoidCJ9&oh=4f99b56bb3dab33d1312bd502ff91974&oe=59C8F9AB"
+ NotificationContent(loadFbCookie(Prefs.userId)!!, 1234,
+ "https://www.google.ca/",
+ this@MainActivity.string(R.string.kau_lorem_ipsum),
+ System.currentTimeMillis(), debugIcon)
+ .createNotification(this@MainActivity, true)
+ }
}
fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
new file mode 100644
index 00000000..6af6b1db
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
@@ -0,0 +1,149 @@
+package com.pitchedapps.frost.services
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.job.JobInfo
+import android.app.job.JobScheduler
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.net.Uri
+import android.support.v4.app.NotificationCompat
+import android.support.v4.app.NotificationManagerCompat
+import ca.allanwang.kau.utils.color
+import ca.allanwang.kau.utils.dpToPx
+import ca.allanwang.kau.utils.string
+import com.bumptech.glide.request.target.SimpleTarget
+import com.bumptech.glide.request.transition.Transition
+import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.FrostWebActivity
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.WebOverlayActivity
+import com.pitchedapps.frost.dbflow.CookieModel
+import com.pitchedapps.frost.dbflow.fetchUsername
+import com.pitchedapps.frost.facebook.FB_URL_BASE
+import com.pitchedapps.frost.utils.GlideApp
+import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.withRoundIcon
+import org.jetbrains.anko.runOnUiThread
+
+/**
+ * Created by Allan Wang on 2017-07-08.
+ */
+val Context.frostNotification: NotificationCompat.Builder
+ get() = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID).apply {
+ setSmallIcon(R.drawable.frost_f_24)
+ setAutoCancel(true)
+ color = color(R.color.frost_notification_accent)
+ }
+
+val NotificationCompat.Builder.withBigText: NotificationCompat.BigTextStyle
+ get() = NotificationCompat.BigTextStyle(this)
+
+/**
+ * Created by Allan Wang on 2017-07-08.
+ *
+ * Custom target to set the content view and update a given notification
+ */
+class FrostNotificationTarget(val context: Context,
+ val notifId: Int,
+ val notifTag: String,
+ val builder: NotificationCompat.Builder
+) : SimpleTarget<Bitmap>(40.dpToPx, 40.dpToPx) {
+
+ override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>) {
+ builder.setLargeIcon(resource)
+ NotificationManagerCompat.from(context).notify(notifTag, notifId, builder.withBigText.build())
+ }
+}
+
+/**
+ * Notification data holder
+ */
+data class NotificationContent(val data: CookieModel,
+ val notifId: Int,
+ val href: String,
+ val text: String,
+ val timestamp: Long,
+ val profileUrl: String) {
+ fun createNotification(context: Context, verifiedUser: Boolean = false) {
+ //in case we haven't found the name, we will try one more time before passing the notification
+ if (!verifiedUser && data.name?.isBlank() ?: true) {
+ data.fetchUsername {
+ data.name = it
+ createNotification(context, true)
+ }
+ } else {
+ val intent = Intent(context, FrostWebActivity::class.java)
+ intent.data = Uri.parse("${FB_URL_BASE}$href")
+ intent.putExtra(WebOverlayActivity.ARG_USER_ID, data.id)
+ val group = "frost_${data.id}"
+ val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
+ val notifBuilder = context.frostNotification
+ .setContentTitle(context.string(R.string.app_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")
+ NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.withBigText.build())
+
+ if (profileUrl.isNotBlank()) {
+ context.runOnUiThread {
+ GlideApp.with(context)
+ .asBitmap()
+ .load(profileUrl)
+ .withRoundIcon()
+ .into(FrostNotificationTarget(context, notifId, group, notifBuilder))
+ }
+ }
+ }
+ }
+}
+
+const val NOTIFICATION_PERIODIC_JOB = 7
+
+/**
+ * [interval] is # of min, which must be at least 15
+ * returns false if an error occurs; true otherwise
+ */
+fun Context.scheduleNotifications(minutes: Long): Boolean {
+ val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
+ scheduler.cancel(NOTIFICATION_PERIODIC_JOB)
+ if (minutes < 0L) return true
+ val serviceComponent = ComponentName(this, NotificationService::class.java)
+ val builder = JobInfo.Builder(NOTIFICATION_PERIODIC_JOB, serviceComponent)
+ .setPeriodic(minutes * 60000)
+ .setPersisted(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //TODO add options
+ val result = scheduler.schedule(builder.build())
+ if (result <= 0) {
+ L.eThrow("Notification scheduler failed")
+ return false
+ }
+ return true
+}
+
+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 serviceComponent = ComponentName(this, NotificationService::class.java)
+ val builder = JobInfo.Builder(NOTIFICATION_JOB_NOW, serviceComponent)
+ .setMinimumLatency(0L)
+ .setOverrideDeadline(2000L)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ val result = scheduler.schedule(builder.build())
+ if (result <= 0) {
+ L.eThrow("Notification scheduler failed")
+ return false
+ }
+ 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 d00ca8f0..4c03f056 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
@@ -1,26 +1,18 @@
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.FrostWebActivity
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.WebOverlayActivity
import com.pitchedapps.frost.dbflow.*
import com.pitchedapps.frost.facebook.FACEBOOK_COM
-import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.frostAnswersCustom
-import com.pitchedapps.frost.utils.frostNotification
import org.jetbrains.anko.doAsync
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
@@ -36,6 +28,12 @@ class NotificationService : JobService() {
var future: Future<Unit>? = null
+ companion object {
+ val epochMatcher: Regex by lazy { Regex(":([0-9]*?),") }
+ val notifIdMatcher: Regex by lazy { Regex("notif_id\":([0-9]*?),") }
+ val profMatcher: Regex by lazy { Regex("url\\(\"(.*?)\"\\)") }
+ }
+
override fun onStopJob(params: JobParameters?): Boolean {
future?.cancel(true)
future = null
@@ -44,27 +42,15 @@ class NotificationService : JobService() {
override fun onStartJob(params: JobParameters?): Boolean {
future = doAsync {
- loadFbCookiesSync().forEach {
- data ->
- L.i("Handle notifications for $data")
- val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).get()
- val unreadNotifications = doc.getElementById("notifications_list").getElementsByClass("aclb")
- var notifCount = 0
- var latestEpoch = lastNotificationTime(data.id)
- L.v("Latest Epoch $latestEpoch")
- unreadNotifications.forEach unread@ {
- elem ->
- val notif = parseNotification(data, elem)
- if (notif != null) {
- if (notif.timestamp <= latestEpoch) return@unread
- notif.createNotification(this@NotificationService)
- latestEpoch = notif.timestamp
- notifCount++
- }
+ if (Prefs.notificationAllAccounts) {
+ loadFbCookiesSync().forEach {
+ data ->
+ fetchNotifications(data)
}
- if (notifCount > 0) saveNotificationTime(NotificationModel(data.id, latestEpoch))
- frostAnswersCustom("Notifications") { putCustomAttribute("Count", notifCount) }
- summaryNotification(data.id, notifCount)
+ } else {
+ val currentCookie = loadFbCookie(Prefs.userId)
+ if (currentCookie != null)
+ fetchNotifications(currentCookie)
}
L.d("Finished notifications")
jobFinished(params, false)
@@ -73,13 +59,35 @@ class NotificationService : JobService() {
return true
}
- companion object {
- val epochMatcher: Regex by lazy { Regex(":([0-9]*),") }
- val notifIdMatcher: Regex by lazy { Regex("notif_id\":([0-9]*),") }
+ fun fetchNotifications(data: CookieModel) {
+ L.i("Notif fetch for $data")
+ val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).get()
+ //aclb for unread, acw for read
+ val unreadNotifications = doc.getElementById("notifications_list").getElementsByClass("aclb")
+ var notifCount = 0
+// val prevLatestEpoch = 1498931565L // for testing
+ val prevLatestEpoch = lastNotificationTime(data.id)
+ L.v("Notif Prev Latest Epoch $prevLatestEpoch")
+ var newLatestEpoch = prevLatestEpoch
+ 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)
+ if (notif.timestamp > newLatestEpoch)
+ newLatestEpoch = notif.timestamp
+ notifCount++
+ }
+ if (newLatestEpoch != prevLatestEpoch) saveNotificationTime(NotificationModel(data.id, newLatestEpoch))
+ frostAnswersCustom("Notifications") { putCustomAttribute("Count", notifCount) }
+ summaryNotification(data.id, notifCount)
}
+
fun parseNotification(data: CookieModel, element: Element): NotificationContent? {
val a = element.getElementsByTag("a").first() ?: return null
+ //fetch id
val dataStore = a.attr("data-store")
val notifId = if (dataStore == null) System.currentTimeMillis()
else notifIdMatcher.find(dataStore)?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis()
@@ -89,48 +97,21 @@ class NotificationService : JobService() {
if (Prefs.notificationKeywords.any { text.contains(it, ignoreCase = true) }) return null //notification filtered out
if (timeString != null) text = text.removeSuffix(timeString)
text = text.trim()
+ //fetch epoch
val abbrData = abbr?.attr("data-store")
val epoch = if (abbrData == null) -1L else epochMatcher.find(abbrData)?.groups?.get(1)?.value?.toLong() ?: -1L
- return NotificationContent(data, notifId.toInt(), a.attr("href"), text, epoch)
+ //fetch profpic
+ val p = element.select("i.img[style*=url]")
+ val pUrl = profMatcher.find(p.getOrNull(0)?.attr("style") ?: "")?.groups?.get(1)?.value ?: ""
+ return NotificationContent(data, notifId.toInt(), a.attr("href"), text, epoch, pUrl)
}
private fun Context.debugNotification(text: String) {
- if (BuildConfig.DEBUG) {
- val notifBuilder = frostNotification
- .setContentTitle(string(R.string.app_name))
- .setContentText(text)
-
- NotificationManagerCompat.from(this).notify(999, notifBuilder.build())
- }
- }
-
- data class NotificationContent(val data: CookieModel, val notifId: Int, val href: String, val text: String, val timestamp: Long) {
- fun createNotification(context: Context, verifiedUser: Boolean = false) {
- //in case we haven't found the name, we will try one more time before passing the notification
- if (!verifiedUser && data.name?.isBlank() ?: true) {
- data.fetchUsername {
- data.name = it
- createNotification(context, true)
- }
- } else {
- val intent = Intent(context, FrostWebActivity::class.java)
- intent.data = Uri.parse("$FB_URL_BASE$href")
- intent.putExtra(WebOverlayActivity.ARG_USER_ID, data.id)
- val group = "frost_${data.id}"
- val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
- val notifBuilder = context.frostNotification
- .setContentTitle(context.string(R.string.app_name))
- .setContentText(text)
- .setContentIntent(pendingIntent)
- .setCategory(Notification.CATEGORY_SOCIAL)
- .setSubText(data.name)
- .setGroup(group)
-
- if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000)
-
- NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.build())
- }
- }
+ if (!BuildConfig.DEBUG) return
+ val notifBuilder = frostNotification
+ .setContentTitle(string(R.string.app_name))
+ .setContentText(text)
+ NotificationManagerCompat.from(this).notify(999, notifBuilder.build())
}
fun summaryNotification(userId: Long, count: Int) {
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 a239e58f..52f7412f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/UpdateReceiver.kt
@@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
-import com.pitchedapps.frost.utils.scheduleNotifications
/**
* Created by Allan Wang on 2017-05-31.
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt
index 03ed517b..5986a998 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt
@@ -2,11 +2,13 @@ package com.pitchedapps.frost.settings
import ca.allanwang.kau.kpref.KPrefAdapterBuilder
import ca.allanwang.kau.utils.minuteToText
+import ca.allanwang.kau.utils.snackbar
import com.pitchedapps.frost.R
import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.services.fetchNotifications
+import com.pitchedapps.frost.services.scheduleNotifications
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.materialDialogThemed
-import com.pitchedapps.frost.utils.scheduleNotifications
import com.pitchedapps.frost.views.Keywords
/**
@@ -49,4 +51,18 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
}
}
+ checkbox(R.string.notification_all_accounts, { Prefs.notificationAllAccounts }, { Prefs.notificationAllAccounts = it }) {
+ descRes = R.string.notification_all_accounts_desc
+ }
+
+ plainText(R.string.notification_fetch_now) {
+ descRes = R.string.notification_fetch_now_desc
+ onClick = {
+ _, _, _ ->
+ snackbar(if (fetchNotifications()) R.string.notification_fetch_success else R.string.notification_fetch_fail)
+ true
+ }
+ }
+
+
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
index 95642a7a..7ce8ca10 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
@@ -85,6 +85,8 @@ object Prefs : KPref() {
var notificationKeywords: StringSet by kpref("notification_keywords", mutableSetOf<String>())
+ var notificationAllAccounts: Boolean by kpref("notification_all_accounts", true)
+
/**
* Cache like value to determine if user has or had pro
* In most cases, [com.pitchedapps.frost.utils.iab.IS_FROST_PRO] should be looked at instead
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
index 6f3459d7..3d3025cf 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
@@ -1,31 +1,29 @@
package com.pitchedapps.frost.utils
import android.app.Activity
-import android.app.job.JobInfo
-import android.app.job.JobScheduler
-import android.content.ComponentName
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.support.annotation.StringRes
import android.support.design.internal.SnackbarContentLayout
import android.support.design.widget.Snackbar
-import android.support.v4.app.NotificationCompat
import android.support.v7.widget.Toolbar
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import ca.allanwang.kau.utils.*
import com.afollestad.materialdialogs.MaterialDialog
+import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.annotation.GlideModule
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.module.AppGlideModule
+import com.bumptech.glide.request.RequestOptions
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.answers.CustomEvent
import com.pitchedapps.frost.*
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.facebook.formattedFbUrl
-import com.pitchedapps.frost.services.NotificationService
/**
* Created by Allan Wang on 2017-06-03.
@@ -64,14 +62,6 @@ fun WebOverlayActivity.url(): String {
return intent.extras?.getString(ARG_URL) ?: FbTab.FEED.url
}
-val Context.frostNotification: NotificationCompat.Builder
- get() = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID).apply {
- setSmallIcon(R.drawable.frost_f_24)
- setAutoCancel(true)
- color = color(R.color.frost_notification_accent)
- }
-
-
fun Context.materialDialogThemed(action: MaterialDialog.Builder.() -> Unit): MaterialDialog {
val builder = MaterialDialog.Builder(this).theme()
builder.action()
@@ -111,29 +101,6 @@ fun Activity.setFrostColors(toolbar: Toolbar? = null, themeWindow: Boolean = tru
backgrounds.forEach { it.setBackgroundColor(Prefs.bgColor) }
}
-
-const val NOTIFICATION_JOB = 7
-/**
- * [interval] is # of min, which must be at least 15
- * returns false if an error occurs; true otherwise
- */
-fun Context.scheduleNotifications(minutes: Long): Boolean {
- val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
- scheduler.cancel(NOTIFICATION_JOB)
- if (minutes < 0L) return true
- val serviceComponent = ComponentName(this, NotificationService::class.java)
- val builder = JobInfo.Builder(NOTIFICATION_JOB, serviceComponent)
- .setPeriodic(minutes * 60000)
- .setPersisted(true)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //TODO add options
- val result = scheduler.schedule(builder.build())
- if (result <= 0) {
- L.eThrow("Notification scheduler failed")
- return false
- }
- return true
-}
-
fun frostAnswers(action: Answers.() -> Unit) {
if (BuildConfig.DEBUG || !Prefs.analytics) return
Answers.getInstance().action()
@@ -163,3 +130,4 @@ fun Activity.frostNavigationBar() {
navigationBarColor = if (Prefs.tintNavBar) Prefs.headerColor else Color.BLACK
}
+fun <T> RequestBuilder<T>.withRoundIcon() = apply(RequestOptions().transform(CircleCrop()))
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt
index 0fb9dbbb..5186b46c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt
@@ -21,6 +21,7 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL
import com.pitchedapps.frost.utils.Prefs
+import com.pitchedapps.frost.utils.withRoundIcon
/**
* Created by Allan Wang on 2017-06-05.
@@ -35,8 +36,7 @@ class AccountItem(val cookie: CookieModel?) : KauIItem<AccountItem, AccountItem.
text.setTextColor(Prefs.textColor)
if (cookie != null) {
text.text = cookie.name
- val options = RequestOptions().transform(CircleCrop())
- Glide.with(itemView).load(PROFILE_PICTURE_URL(cookie.id)).apply(options).listener(object : RequestListener<Drawable> {
+ Glide.with(itemView).load(PROFILE_PICTURE_URL(cookie.id)).withRoundIcon().listener(object : RequestListener<Drawable> {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
text.fadeIn()
return false
diff --git a/app/src/main/res/values/strings_preferences b/app/src/main/res/values/strings_preferences
index e9cc643c..f70ffe59 100644
--- a/app/src/main/res/values/strings_preferences
+++ b/app/src/main/res/values/strings_preferences
@@ -44,6 +44,12 @@
<string name="add_keyword">Add Keyword</string>
<string name="hint_keyword">Type keyword and press +</string>
<string name="empty_keyword">Empty Keyword</string>
+ <string name="notification_all_accounts">Notify from all accounts</string>
+ <string name="notification_all_accounts_desc">Get notifications for every account that is logged in. Disabling this will only fetch notifications form the currently selected account.</string>
+ <string name="notification_fetch_now">Fetch Notifications Now</string>
+ <string name="notification_fetch_now_desc">Trigger the notification fetcher one time.</string>
+ <string name="notification_fetch_success">Fetching Notifications…</string>
+ <string name="notification_fetch_fail">Couldn\'t fetch notifications</string>
<string name="fancy_animations">Fancy Animations</string>
<string name="fancy_animations_desc">Reveal webviews using ripples and animate transitions</string>
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index b1bf7a5c..1a332ea4 100644
--- a/app/src/main/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
@@ -2,10 +2,25 @@
<resources>
<!--
+ <version title="Beta Updates"/>
+ <item text="" />
<version title="v"/>
<item text="" />
-->
+ <version title="Beta Updates"/>
+ <item text="" />
+ <!--<version title="v1.3"/>-->
+ <item text="Create toggle for notifications only from primary account" />
+ <item text="Micro string optimizations" />
+ <item text="Add profile icons to notifications" />
+ <item text="Make notifications expandable" />
+ <item text="Add notification trigger in settings" />
+ <item text="Fix bug where only single latest notification is showing" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+
<version title="v1.2"/>
<item text="Scale browser on keyboard pop up" />
<item text="Clean up web overlay" />