1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
package com.pitchedapps.frost.services
import android.app.job.JobParameters
import android.app.job.JobService
import android.content.Context
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.dbflow.*
import com.pitchedapps.frost.facebook.FACEBOOK_COM
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 org.jetbrains.anko.doAsync
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.concurrent.Future
/**
* Created by Allan Wang on 2017-06-14.
*
* Service to manage notifications
* Will periodically check through all accounts in the db and send notifications when appropriate
*/
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
return false
}
override fun onStartJob(params: JobParameters?): Boolean {
future = doAsync {
if (Prefs.notificationAllAccounts) {
loadFbCookiesSync().forEach {
data ->
fetchNotifications(data)
}
} else {
val currentCookie = loadFbCookie(Prefs.userId)
if (currentCookie != null)
fetchNotifications(currentCookie)
}
L.d("Finished notifications")
jobFinished(params, false)
future = null
}
return true
}
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()
val abbr = element.getElementsByTag("abbr")
val timeString = abbr?.text()
var text = a.text().replace("\u00a0", " ") //remove
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
//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) 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) {
if (count <= 1) return
val notifBuilder = frostNotification
.setContentTitle(string(R.string.app_name))
.setContentText("$count notifications")
.setGroup("frost_$userId")
.setGroupSummary(true)
NotificationManagerCompat.from(this).notify("frost_$userId", userId.toInt(), notifBuilder.build())
}
}
|