aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
blob: cd66b9c2fb00dce7d2c34cc7ffb95bc928646a15 (plain)
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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.os.Looper
import android.support.v4.app.ActivityOptionsCompat
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import ca.allanwang.kau.utils.checkThread
import ca.allanwang.kau.utils.string
import com.pitchedapps.frost.BuildConfig
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.ARG_URL
import com.pitchedapps.frost.utils.L
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.
 */
class NotificationService : JobService() {

    var future: Future<Unit>? = null

    override fun onStopJob(params: JobParameters?): Boolean {
        future?.cancel(true)
        future = null
        return false
    }

    override fun onStartJob(params: JobParameters?): Boolean {
        future = doAsync {
            debugNotification("Load notifs")
            loadFbCookiesSync().forEach {
                data ->
                L.i("Handling notifications for ${data.id}")
                L.v("Using data $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 (notifCount > 0) saveNotificationTime(NotificationModel(data.id, latestEpoch))
                summaryNotification(data.id, notifCount)
            }
            L.d("Finished notifications")
            jobFinished(params, false)
        }
        return true
    }

    companion object {
        const val ARG_ID = "arg_id"
        val epochMatcher: Regex by lazy { Regex(":([0-9]*),") }
        val notifIdMatcher: Regex by lazy { Regex("notif_id\":([0-9]*),") }
    }

    fun parseNotification(data: CookieModel, element: Element): NotificationContent? {
        val a = element.getElementsByTag("a").first() ?: return null
        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 &nbsp;
        if (timeString != null) text = text.removeSuffix(timeString)
        text = text.trim()
        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)
    }

    private fun Context.debugNotification(text: String) {
        if (BuildConfig.DEBUG) {
            val notifBuilder = NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.frost_f_24)
                    .setContentTitle(string(R.string.app_name))
                    .setContentText(text)
                    .setAutoCancel(true)

            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) {
            val intent = Intent(context, WebOverlayActivity::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
            intent.putExtra(ARG_URL, "$FB_URL_BASE$href")
            intent.action = System.currentTimeMillis().toString() //dummy action
            val group = "frost_${data.id}"
            val bundle = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.slide_in_right, R.anim.slide_out_right).toBundle()
            val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT, bundle)
            val notifBuilder = NotificationCompat.Builder(context)
                    .setSmallIcon(R.drawable.frost_f_24)
                    .setContentTitle(context.string(R.string.app_name))
                    .setContentText(text)
                    .setContentIntent(pendingIntent)
                    .setCategory(Notification.CATEGORY_SOCIAL)
                    .setSubText(data.name)
                    .setGroup(group)
                    .setAutoCancel(true)
//                    .setColor(context.color(R.color.facebook_blue))

            if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000)

            NotificationManagerCompat.from(context).notify(group, notifId, notifBuilder.build())
        }
    }

    fun summaryNotification(userId: Long, count: Int) {
        if (count <= 1) return
        val notifBuilder = NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.frost_f_24)
                .setContentTitle(string(R.string.app_name))
                .setContentText("$count notifications")
                .setGroup("frost_$userId")
                .setGroupSummary(true)
                .setAutoCancel(true)

        NotificationManagerCompat.from(this).notify("frost_$userId", userId.toInt(), notifBuilder.build())
    }

}