aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt
blob: 707220f64905003c7f8ba32a1669f05efd106a60 (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
package com.pitchedapps.frost.services

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.PersistableBundle
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import ca.allanwang.kau.utils.color
import ca.allanwang.kau.utils.string
import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs

/**
 * Created by Allan Wang on 07/04/18.
 */
const val NOTIF_CHANNEL_GENERAL = "general"
const val NOTIF_CHANNEL_MESSAGES = "messages"

fun setupNotificationChannels(c: Context) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
    val manager = c.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    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) }
    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" }
}

@RequiresApi(Build.VERSION_CODES.O)
private fun NotificationManager.createNotificationChannel(id: String, name: String): NotificationChannel {
    val channel = NotificationChannel(id,
            name, NotificationManager.IMPORTANCE_DEFAULT)
    channel.enableLights(true)
    channel.lightColor = Prefs.accentColor
    channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
    createNotificationChannel(channel)
    return channel
}

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)
                }

/**
 * Dictates whether a notification should have sound/vibration/lights or not
 * Delegates to channels if Android O and up
 * Otherwise uses our provided preferences
 */
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)
    } else if (!enable) {
        setDefaults(0)
    } else {
        var defaults = 0
        if (Prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE
        if (Prefs.notificationSound) {
            if (ringtone.isNotBlank()) setSound(Uri.parse(ringtone))
            else defaults = defaults or Notification.DEFAULT_SOUND
        }
        if (Prefs.notificationLights) defaults = defaults or Notification.DEFAULT_LIGHTS
        setDefaults(defaults)
    }
    return this
}

/*
 * -----------------------------------
 * Job Scheduler
 * -----------------------------------
 */

const val NOTIFICATION_PARAM_ID = "notif_param_id"

fun JobInfo.Builder.setExtras(id: Int): JobInfo.Builder {
    val bundle = PersistableBundle()
    bundle.putInt(NOTIFICATION_PARAM_ID, id)
    return setExtras(bundle)
}

/**
 * interval is # of min, which must be at least 15
 * returns false if an error occurs; true otherwise
 */
inline fun <reified T : JobService> Context.scheduleJob(id: Int, minutes: Long): Boolean {
    val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    scheduler.cancel(id)
    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
    val result = scheduler.schedule(builder.build())
    if (result <= 0) {
        L.eThrow("${T::class.java.simpleName} scheduler failed")
        return false
    }
    return true
}

/**
 * Run notification job right now
 */
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)
    val result = scheduler.schedule(builder.build())
    if (result <= 0) {
        L.eThrow("${T::class.java.simpleName} instant scheduler failed")
        return false
    }
    return true
}