Implement optional separate app notification channels
This commit is contained in:
@@ -1,56 +1,92 @@
|
||||
package com.github.gotify
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationChannelGroup
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.gotify.client.model.Application
|
||||
import com.github.gotify.log.Log
|
||||
|
||||
internal object NotificationSupport {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun createChannels(notificationManager: NotificationManager) {
|
||||
try {
|
||||
// Low importance so that persistent notification can be sorted towards bottom of
|
||||
// notification shade. Also prevents vibrations caused by persistent notification
|
||||
val foreground = NotificationChannel(
|
||||
Channel.FOREGROUND,
|
||||
"Gotify foreground notification",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
foreground.setShowBadge(false)
|
||||
fun createForegroundChannel(context: Context, notificationManager: NotificationManager) {
|
||||
// Low importance so that persistent notification can be sorted towards bottom of
|
||||
// notification shade. Also prevents vibrations caused by persistent notification
|
||||
val foreground = NotificationChannel(
|
||||
Channel.FOREGROUND,
|
||||
context.getString(R.string.notification_channel_title_foreground),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
setShowBadge(false)
|
||||
}
|
||||
notificationManager.createNotificationChannel(foreground)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun createChannels(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager,
|
||||
applications: List<Application>
|
||||
) {
|
||||
if (areAppChannelsRequested(context)) {
|
||||
notificationManager.notificationChannels.forEach { channel ->
|
||||
if (channel.id != Channel.FOREGROUND) {
|
||||
notificationManager.deleteNotificationChannel(channel.id)
|
||||
}
|
||||
}
|
||||
applications.forEach { app ->
|
||||
createAppChannels(context, notificationManager, app.id.toString(), app.name)
|
||||
}
|
||||
} else {
|
||||
notificationManager.notificationChannelGroups.forEach { group ->
|
||||
notificationManager.deleteNotificationChannelGroup(group.id)
|
||||
}
|
||||
createGeneralChannels(context, notificationManager)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun createGeneralChannels(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager
|
||||
) {
|
||||
try {
|
||||
val messagesImportanceMin = NotificationChannel(
|
||||
Channel.MESSAGES_IMPORTANCE_MIN,
|
||||
"Min priority messages (<1)",
|
||||
context.getString(R.string.notification_channel_title_min),
|
||||
NotificationManager.IMPORTANCE_MIN
|
||||
)
|
||||
|
||||
val messagesImportanceLow = NotificationChannel(
|
||||
Channel.MESSAGES_IMPORTANCE_LOW,
|
||||
"Low priority messages (1-3)",
|
||||
context.getString(R.string.notification_channel_title_low),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
|
||||
val messagesImportanceDefault = NotificationChannel(
|
||||
Channel.MESSAGES_IMPORTANCE_DEFAULT,
|
||||
"Normal priority messages (4-7)",
|
||||
context.getString(R.string.notification_channel_title_normal),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
messagesImportanceDefault.enableLights(true)
|
||||
messagesImportanceDefault.lightColor = Color.CYAN
|
||||
messagesImportanceDefault.enableVibration(true)
|
||||
).apply {
|
||||
enableLights(true)
|
||||
lightColor = Color.CYAN
|
||||
enableVibration(true)
|
||||
}
|
||||
|
||||
val messagesImportanceHigh = NotificationChannel(
|
||||
Channel.MESSAGES_IMPORTANCE_HIGH,
|
||||
"High priority messages (>7)",
|
||||
context.getString(R.string.notification_channel_title_high),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
messagesImportanceHigh.enableLights(true)
|
||||
messagesImportanceHigh.lightColor = Color.CYAN
|
||||
messagesImportanceHigh.enableVibration(true)
|
||||
).apply {
|
||||
enableLights(true)
|
||||
lightColor = Color.CYAN
|
||||
enableVibration(true)
|
||||
}
|
||||
|
||||
notificationManager.createNotificationChannel(foreground)
|
||||
notificationManager.createNotificationChannel(messagesImportanceMin)
|
||||
notificationManager.createNotificationChannel(messagesImportanceLow)
|
||||
notificationManager.createNotificationChannel(messagesImportanceDefault)
|
||||
@@ -60,6 +96,88 @@ internal object NotificationSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
fun createChannelIfNonexistent(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager,
|
||||
groupId: String,
|
||||
groupName: String,
|
||||
channelId: String
|
||||
) {
|
||||
if (!doesNotificationChannelExist(context, channelId)) {
|
||||
createAppChannels(context, notificationManager, groupId, groupName)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun createAppChannels(
|
||||
context: Context,
|
||||
notificationManager: NotificationManager,
|
||||
groupId: String,
|
||||
groupName: String
|
||||
) {
|
||||
try {
|
||||
notificationManager.createNotificationChannelGroup(
|
||||
NotificationChannelGroup(
|
||||
groupId,
|
||||
groupName
|
||||
)
|
||||
)
|
||||
|
||||
val messagesImportanceMin = NotificationChannel(
|
||||
getChannelID(Channel.MESSAGES_IMPORTANCE_MIN, groupId),
|
||||
context.getString(R.string.notification_channel_title_min),
|
||||
NotificationManager.IMPORTANCE_MIN
|
||||
).apply {
|
||||
group = groupId
|
||||
}
|
||||
|
||||
val messagesImportanceLow = NotificationChannel(
|
||||
getChannelID(Channel.MESSAGES_IMPORTANCE_LOW, groupId),
|
||||
context.getString(R.string.notification_channel_title_low),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
group = groupId
|
||||
}
|
||||
|
||||
val messagesImportanceDefault = NotificationChannel(
|
||||
getChannelID(Channel.MESSAGES_IMPORTANCE_DEFAULT, groupId),
|
||||
context.getString(R.string.notification_channel_title_normal),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
).apply {
|
||||
enableLights(true)
|
||||
lightColor = Color.CYAN
|
||||
enableVibration(true)
|
||||
group = groupId
|
||||
}
|
||||
|
||||
val messagesImportanceHigh = NotificationChannel(
|
||||
getChannelID(Channel.MESSAGES_IMPORTANCE_HIGH, groupId),
|
||||
context.getString(R.string.notification_channel_title_high),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
).apply {
|
||||
enableLights(true)
|
||||
lightColor = Color.CYAN
|
||||
enableVibration(true)
|
||||
group = groupId
|
||||
}
|
||||
|
||||
notificationManager.createNotificationChannel(messagesImportanceMin)
|
||||
notificationManager.createNotificationChannel(messagesImportanceLow)
|
||||
notificationManager.createNotificationChannel(messagesImportanceDefault)
|
||||
notificationManager.createNotificationChannel(messagesImportanceHigh)
|
||||
} catch (e: Exception) {
|
||||
Log.e("Could not create channel", e)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private fun doesNotificationChannelExist(context: Context, channelId: String): Boolean {
|
||||
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val channel = manager.getNotificationChannel(channelId)
|
||||
return channel != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Map {@link com.github.gotify.client.model.Message#getPriority() Gotify message priorities to
|
||||
* Android channels.
|
||||
@@ -87,6 +205,21 @@ internal object NotificationSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getChannelID(importance: String, groupId: String): String {
|
||||
return "$importance::$groupId"
|
||||
}
|
||||
|
||||
fun getChannelID(priority: Long, groupId: String): String {
|
||||
return getChannelID(convertPriorityToChannel(priority), groupId)
|
||||
}
|
||||
|
||||
fun areAppChannelsRequested(context: Context): Boolean {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
|
||||
context.getString(R.string.setting_key_notification_channels),
|
||||
context.resources.getBoolean(R.bool.notification_channels)
|
||||
)
|
||||
}
|
||||
|
||||
object Group {
|
||||
const val MESSAGES = "GOTIFY_GROUP_MESSAGES"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.github.gotify.init
|
||||
|
||||
import android.Manifest
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -41,8 +42,9 @@ internal class InitializationActivity : AppCompatActivity() {
|
||||
ThemeHelper.setTheme(this, theme)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationSupport.createChannels(
|
||||
this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
NotificationSupport.createForegroundChannel(
|
||||
this,
|
||||
(this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
|
||||
)
|
||||
}
|
||||
UncaughtExceptionHandler.registerCurrentThread()
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.graphics.Canvas
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
@@ -28,6 +29,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.gotify.BuildConfig
|
||||
import com.github.gotify.MissedMessageUtil
|
||||
import com.github.gotify.NotificationSupport
|
||||
import com.github.gotify.R
|
||||
import com.github.gotify.Utils
|
||||
import com.github.gotify.Utils.launchCoroutine
|
||||
@@ -204,6 +206,14 @@ internal class MessagesActivity :
|
||||
.into(t)
|
||||
}
|
||||
selectAppInMenu(selectedItem)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationSupport.createChannels(
|
||||
this,
|
||||
(this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager),
|
||||
applications
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDrawer() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.Color
|
||||
@@ -312,13 +313,26 @@ internal class WebSocketService : Service() {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val b = NotificationCompat.Builder(
|
||||
this,
|
||||
NotificationSupport.convertPriorityToChannel(priority)
|
||||
)
|
||||
val channelId: String
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||
NotificationSupport.areAppChannelsRequested(this)
|
||||
) {
|
||||
channelId = NotificationSupport.getChannelID(priority, appId.toString())
|
||||
NotificationSupport.createChannelIfNonexistent(
|
||||
this,
|
||||
(this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager),
|
||||
appId.toString(),
|
||||
appId.toString(),
|
||||
channelId
|
||||
)
|
||||
} else {
|
||||
channelId = NotificationSupport.convertPriorityToChannel(priority)
|
||||
}
|
||||
|
||||
val b = NotificationCompat.Builder(this, channelId)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
showNotificationGroup(priority)
|
||||
showNotificationGroup(channelId)
|
||||
}
|
||||
|
||||
b.setAutoCancel(true)
|
||||
@@ -365,7 +379,7 @@ internal class WebSocketService : Service() {
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun showNotificationGroup(priority: Long) {
|
||||
fun showNotificationGroup(channelId: String) {
|
||||
val intent = Intent(this, MessagesActivity::class.java)
|
||||
val contentIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
@@ -376,7 +390,7 @@ internal class WebSocketService : Service() {
|
||||
|
||||
val builder = NotificationCompat.Builder(
|
||||
this,
|
||||
NotificationSupport.convertPriorityToChannel(priority)
|
||||
channelId
|
||||
)
|
||||
|
||||
builder.setAutoCancel(true)
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
@@ -14,6 +15,7 @@ import androidx.preference.ListPreferenceDialogFragmentCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.github.gotify.R
|
||||
import com.github.gotify.databinding.SettingsActivityBinding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@@ -48,17 +50,36 @@ internal class SettingsActivity : AppCompatActivity(), OnSharedPreferenceChangeL
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
if (getString(R.string.setting_key_theme) == key) {
|
||||
ThemeHelper.setTheme(
|
||||
this,
|
||||
sharedPreferences.getString(key, getString(R.string.theme_default))!!
|
||||
)
|
||||
when (key) {
|
||||
getString(R.string.setting_key_theme) -> {
|
||||
ThemeHelper.setTheme(
|
||||
this,
|
||||
sharedPreferences.getString(key, getString(R.string.theme_default))!!
|
||||
)
|
||||
}
|
||||
getString(R.string.setting_key_notification_channels) -> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.setting_notification_channels_dialog_title)
|
||||
.setMessage(R.string.setting_notification_channels_dialog_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
||||
if (!isFinishing) {
|
||||
dialogBuilder.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
findPreference<SwitchPreferenceCompat>(
|
||||
getString(R.string.setting_key_notification_channels)
|
||||
)?.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
@@ -34,4 +34,5 @@
|
||||
</string-array>
|
||||
<string name="time_format_value_absolute">time_format_absolute</string>
|
||||
<string name="time_format_value_relative">time_format_relative</string>
|
||||
<bool name="notification_channels">false</bool>
|
||||
</resources>
|
||||
|
||||
@@ -80,6 +80,11 @@
|
||||
<string name="setting_message_layout_dialog_button2">Later</string>
|
||||
<string name="setting_time_format">Time format</string>
|
||||
<string name="setting_key_time_format">time_format</string>
|
||||
<string name="setting_notifications">Notifications</string>
|
||||
<string name="setting_notification_channels">Separate app notification channels</string>
|
||||
<string name="setting_key_notification_channels">notification_channels</string>
|
||||
<string name="setting_notification_channels_dialog_title">Manual refresh required</string>
|
||||
<string name="setting_notification_channels_dialog_message">For the change to take effect you must manually refresh the apps using the refresh button in the main menu.</string>
|
||||
<string name="push_message">Push message</string>
|
||||
<string name="appListDescription">App:</string>
|
||||
<string name="priorityDescription">Priority:</string>
|
||||
@@ -97,4 +102,10 @@
|
||||
<item quantity="one">in %d minute</item>
|
||||
<item quantity="other">in %d minutes</item>
|
||||
</plurals>
|
||||
|
||||
<string name="notification_channel_title_foreground">Gotify foreground notification</string>
|
||||
<string name="notification_channel_title_min">Min priority messages (<1)</string>
|
||||
<string name="notification_channel_title_low">Low priority messages (1–3)</string>
|
||||
<string name="notification_channel_title_normal">Normal priority messages (4–7)</string>
|
||||
<string name="notification_channel_title_high">High priority messages (>7)</string>
|
||||
</resources>
|
||||
|
||||
@@ -24,4 +24,13 @@
|
||||
android:title="@string/setting_time_format" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/setting_notifications" >
|
||||
<SwitchPreferenceCompat
|
||||
android:enabled="false"
|
||||
android:defaultValue="@bool/notification_channels"
|
||||
android:key="@string/setting_key_notification_channels"
|
||||
android:title="@string/setting_notification_channels"
|
||||
app:singleLineTitle="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
Reference in New Issue
Block a user