supports grouped notification channels

This commit is contained in:
Mariotaku Lee 2017-08-26 00:58:47 +08:00
parent 1d81c8cdf8
commit c21e6ebcb5
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
7 changed files with 118 additions and 54 deletions

View File

@ -6,7 +6,7 @@ buildscript {
maven { url 'https://maven.google.com' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-beta2'
classpath 'com.android.tools.build:gradle:3.0.0-beta3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

View File

@ -134,7 +134,7 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
}
super.onCreate()
EmojioneTranslator.init(this)
NotificationChannelsManager.createChannels(this)
NotificationChannelsManager.initialize(this)
applyLanguageSettings()
startKovenant()
initializeAsyncTask()
@ -159,8 +159,11 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
Analyzer.preferencesChanged(sharedPreferences)
DataSyncProvider.Factory.notifyUpdate(this)
NotificationChannelsManager.updateAccountChannelsAndGroups(this)
}
override fun onConfigurationChanged(newConfig: Configuration?) {
applyLanguageSettings()
super.onConfigurationChanged(newConfig)
@ -336,6 +339,10 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
})
}
private fun updateAccountNotificationGroup() {
}
companion object {
private val KEY_UCD_DATA_PROFILING = "ucd_data_profiling"

View File

@ -20,12 +20,24 @@
package org.mariotaku.twidere.extension.model
import android.content.Context
import android.net.Uri
import android.support.v4.app.NotificationCompat
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
/**
* Created by mariotaku on 2017/8/25.
*/
fun NotificationChannelSpec.notificationBuilder(context: Context): NotificationCompat.Builder {
return NotificationCompat.Builder(context, id)
}
fun NotificationChannelSpec.accountNotificationBuilder(context: Context, accountKey: UserKey): NotificationCompat.Builder {
if (!grouped) throw IllegalArgumentException("Requires grouped channel")
return NotificationCompat.Builder(context, accountKey.notificationChannelId(id)).setGroup(accountKey.notificationChannelGroupId())
}
fun UserKey.notificationChannelId(id: String): String {
return "${id}_${Uri.encode(toString())}"
}
fun UserKey.notificationChannelGroupId(): String {
return Uri.encode(toString())
}

View File

@ -31,7 +31,8 @@ enum class NotificationChannelSpec(
@StringRes val nameRes: Int,
@StringRes val descriptionRes: Int = 0,
val importance: Int,
val showBadge: Boolean = false) {
val showBadge: Boolean = false,
val grouped: Boolean = false) {
/**
* For notifications send by app itself.
* Such as "what's new"
@ -53,37 +54,31 @@ enum class NotificationChannelSpec(
serviceStatuses("service_statuses", R.string.notification_channel_name_service_statuses,
importance = NotificationManager.IMPORTANCE_MIN),
/**
* For import notifications related to micro-blogging features.
* Such as failure to update status.
*/
contentNotices("content_notices", R.string.notification_channel_name_content_notices,
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true),
/**
* For updates related to micro-blogging features.
* Such as new statuses posted by friends.
*/
contentUpdates("content_updates", R.string.notification_channel_name_content_updates,
importance = NotificationManager.IMPORTANCE_DEFAULT, showBadge = true),
importance = NotificationManager.IMPORTANCE_DEFAULT, showBadge = true, grouped = true),
/**
* For updates related to micro-blogging features.
* Such as new statuses posted by friends user subscribed to.
*/
contentSubscriptions("content_subscriptions", R.string.notification_channel_name_content_subscriptions,
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true),
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true, grouped = true),
/**
* For interactions related to micro-blogging features.
* Such as replies and likes.
*/
contentInteractions("content_interactions", R.string.notification_channel_name_content_interactions,
descriptionRes = R.string.notification_channel_description_content_interactions,
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true),
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true, grouped = true),
/**
* For messages related to micro-blogging features.
* Such as direct messages.
*/
contentMessages("content_messages", R.string.notification_channel_name_content_messages,
descriptionRes = R.string.notification_channel_description_content_messages,
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true)
importance = NotificationManager.IMPORTANCE_HIGH, showBadge = true, grouped = true)
}

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.util.notification
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
@ -45,14 +46,12 @@ import org.mariotaku.twidere.annotation.NotificationType
import org.mariotaku.twidere.constant.IntentConstants
import org.mariotaku.twidere.constant.iWantMyStarsBackKey
import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.extension.model.*
import org.mariotaku.twidere.extension.model.api.formattedTextWithIndices
import org.mariotaku.twidere.extension.model.getSummaryText
import org.mariotaku.twidere.extension.model.getTitle
import org.mariotaku.twidere.extension.model.notificationBuilder
import org.mariotaku.twidere.extension.model.notificationDisabled
import org.mariotaku.twidere.extension.rawQuery
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
@ -135,7 +134,8 @@ class ContentNotificationManager(
}
// Setup notification
val builder = NotificationChannelSpec.contentUpdates.notificationBuilder(context)
val builder = NotificationChannelSpec.contentUpdates.accountNotificationBuilder(context,
accountKey)
builder.setAutoCancel(true)
builder.setSmallIcon(R.drawable.ic_stat_twitter)
builder.setTicker(notificationTitle)
@ -148,7 +148,6 @@ class ContentNotificationManager(
accountKey, positionKey, false))
builder.setNumber(statusesCount)
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
builder.setGroup("timeline")
applyNotificationPreferences(builder, pref, pref.homeTimelineNotificationType)
try {
val notificationId = Utils.getNotificationId(NOTIFICATION_ID_HOME_TIMELINE, accountKey)
@ -165,8 +164,10 @@ class ContentNotificationManager(
}
fun showInteractions(pref: AccountPreferences, position: Long) {
val am = AccountManager.get(context)
val cr = context.contentResolver
val accountKey = pref.accountKey
val account = AccountUtils.getAccountDetails(am, accountKey, false) ?: return
val where = Expression.and(
Expression.equalsArgs(Activities.ACCOUNT_KEY),
Expression.greaterThan(Activities.POSITION_KEY, position),
@ -176,7 +177,8 @@ class ContentNotificationManager(
@SuppressLint("Recycle")
val c = cr.query(Activities.AboutMe.CONTENT_URI, Activities.COLUMNS, where, whereArgs,
OrderBy(Activities.TIMESTAMP, false).sql) ?: return
val builder = NotificationChannelSpec.contentInteractions.notificationBuilder(context)
val builder = NotificationChannelSpec.contentInteractions.accountNotificationBuilder(context,
accountKey)
val pebbleNotificationStringBuilder = StringBuilder()
try {
val count = c.count
@ -186,7 +188,7 @@ class ContentNotificationManager(
applyNotificationPreferences(builder, pref, pref.mentionsNotificationType)
val resources = context.resources
val accountName = DataStoreUtils.getAccountDisplayName(context, accountKey, nameFirst)
val accountName = userColorNameManager.getDisplayName(account.user, nameFirst)
builder.setContentText(accountName)
val style = NotificationCompat.InboxStyle()
builder.setStyle(style)
@ -246,7 +248,6 @@ class ContentNotificationManager(
builder.setContentTitle(title)
style.setBigContentTitle(title)
builder.setNumber(displayCount)
builder.setGroup("interactions")
builder.setContentIntent(getContentIntent(context, CustomTabType.NOTIFICATIONS_TIMELINE,
NotificationType.INTERACTIONS, accountKey, newMaxPositionKey))
builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.INTERACTIONS,
@ -289,7 +290,8 @@ class ContentNotificationManager(
}
if (messageSum == 0) return
val builder = NotificationChannelSpec.contentMessages.notificationBuilder(context)
val builder = NotificationChannelSpec.contentMessages.accountNotificationBuilder(context,
accountKey)
applyNotificationPreferences(builder, pref, pref.directMessagesNotificationType)
builder.setSmallIcon(R.drawable.ic_stat_message)
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL)
@ -324,7 +326,6 @@ class ContentNotificationManager(
style.addLine(context.getString(R.string.and_N_more, remaining))
}
val notificationId = Utils.getNotificationId(NOTIFICATION_ID_DIRECT_MESSAGES, accountKey)
builder.setGroup("direct_messages")
notificationManager.notify("direct_messages", notificationId, builder.build())
} finally {
cur.close()
@ -337,7 +338,8 @@ class ContentNotificationManager(
val userDisplayName = userColorNameManager.getDisplayName(status.user,
preferences[nameFirstKey])
val statusUri = LinkCreator.getTwidereStatusLink(accountKey, status.id)
val builder = NotificationChannelSpec.contentSubscriptions.notificationBuilder(context)
val builder = NotificationChannelSpec.contentSubscriptions.accountNotificationBuilder(context,
accountKey)
builder.color = userColorNameManager.getUserColor(userKey)
builder.setAutoCancel(true)
builder.setWhen(status.createdAt?.time ?: 0)
@ -355,7 +357,6 @@ class ContentNotificationManager(
}, PendingIntent.FLAG_UPDATE_CURRENT))
val tag = "$accountKey:$userKey:${status.id}"
builder.setGroup("user_notif_$accountKey:$userKey")
notificationManager.notify(tag, NOTIFICATION_ID_USER_NOTIFICATION, builder.build())
}
@ -382,7 +383,7 @@ class ContentNotificationManager(
uriBuilder.scheme(SCHEME_TWIDERE)
uriBuilder.authority(AUTHORITY_DRAFTS)
intent.data = uriBuilder.build()
val nb = NotificationChannelSpec.contentNotices.notificationBuilder(context)
val nb = NotificationChannelSpec.appNotices.notificationBuilder(context)
nb.setTicker(message)
nb.setContentTitle(title)
nb.setContentText(item.text)

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.util.notification
import android.accounts.AccountManager
import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
@ -27,53 +28,102 @@ import android.content.Context
import android.os.Build
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.constant.nameFirstKey
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.extension.model.notificationChannelGroupId
import org.mariotaku.twidere.extension.model.notificationChannelId
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.util.dagger.DependencyHolder
/**
* Created by mariotaku on 2017/8/25.
*/
object NotificationChannelsManager {
fun createChannels(context: Context) {
fun initialize(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
NotificationChannelCreatorImpl.createChannels(context)
NotificationChannelManagerImpl.initialize(context)
}
fun createAccountGroup(context: Context, account: AccountDetails) {
fun updateAccountChannelsAndGroups(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
NotificationChannelCreatorImpl.createAccountGroup(context, account)
NotificationChannelManagerImpl.updateAccountChannelsAndGroups(context)
}
@TargetApi(Build.VERSION_CODES.O)
private object NotificationChannelCreatorImpl {
private object NotificationChannelManagerImpl {
fun createChannels(context: Context) {
fun initialize(context: Context) {
val nm = context.getSystemService(NotificationManager::class.java)
val values = NotificationChannelSpec.values()
nm.notificationChannels.filterNot { channel ->
values.any { channel.id == it.id }
}.forEach {
nm.deleteNotificationChannel(it.id)
}
for (spec in values) {
val addedChannels = mutableListOf<String>()
for (spec in NotificationChannelSpec.values()) {
if (spec.grouped) continue
val channel = NotificationChannel(spec.id, context.getString(spec.nameRes), spec.importance)
if (spec.descriptionRes != 0) {
channel.description = context.getString(spec.descriptionRes)
}
channel.setShowBadge(spec.showBadge)
nm.createNotificationChannel(channel)
addedChannels.add(channel.id)
}
nm.notificationChannels.forEach {
if (it.id !in addedChannels && it.group == null) {
nm.deleteNotificationChannel(it.id)
}
}
}
fun createAccountGroup(context: Context, account: AccountDetails) {
val nm = context.getSystemService(NotificationManager::class.java)
fun updateAccountChannelsAndGroups(context: Context) {
val holder = DependencyHolder.get(context)
val am = AccountManager.get(context)
val nm = context.getSystemService(NotificationManager::class.java)
val pref = holder.preferences
val ucnm = holder.userColorNameManager
val group = NotificationChannelGroup(account.key.toString(),
ucnm.getDisplayName(account.user, pref[nameFirstKey]))
nm.createNotificationChannelGroup(group)
val accounts = AccountUtils.getAllAccountDetails(am, false)
val specs = NotificationChannelSpec.values()
val addedChannels = mutableListOf<String>()
val addedGroups = mutableListOf<String>()
accounts.forEach { account ->
val group = NotificationChannelGroup(account.key.notificationChannelGroupId(),
ucnm.getDisplayName(account.user, pref[nameFirstKey]))
addedGroups.add(group.id)
nm.createNotificationChannelGroup(group)
for (spec in specs) {
if (!spec.grouped) continue
val channel = NotificationChannel(account.key.notificationChannelId(spec.id),
context.getString(spec.nameRes), spec.importance)
if (spec.descriptionRes != 0) {
channel.description = context.getString(spec.descriptionRes)
}
channel.group = group.id
channel.setShowBadge(spec.showBadge)
nm.createNotificationChannel(channel)
addedChannels.add(channel.id)
}
}
// Delete all channels and groups of non-existing accounts
nm.notificationChannels.forEach {
if (it.id !in addedChannels && it.group != null) {
nm.deleteNotificationChannel(it.id)
}
}
nm.notificationChannelGroups.forEach {
if (it.id !in addedGroups) {
nm.deleteNotificationChannelGroup(it.id)
}
}
}
}
}

View File

@ -774,11 +774,10 @@
<string name="notification_channel_description_content_messages">Important messages like DMs</string>
<string name="notification_channel_name_app_notices">App notices</string>
<string name="notification_channel_name_background_progresses">Background operations</string>
<string name="notification_channel_name_content_interactions">Content interactions</string>
<string name="notification_channel_name_content_messages">Content messages</string>
<string name="notification_channel_name_content_notices">Content notices</string>
<string name="notification_channel_name_content_subscriptions">Content subscriptions</string>
<string name="notification_channel_name_content_updates">Content updates</string>
<string name="notification_channel_name_content_interactions">Interactions</string>
<string name="notification_channel_name_content_messages">Messages</string>
<string name="notification_channel_name_content_subscriptions">Subscriptions</string>
<string name="notification_channel_name_content_updates">Updates</string>
<string name="notification_channel_name_service_statuses">Service statuses</string>
<string name="notification_direct_message"><xliff:g id="user">%s</xliff:g> sent you a direct message.</string>
<string name="notification_direct_message_multiple_messages"><xliff:g id="user">%1$s</xliff:g> sent you <xliff:g id="messages_count">%2$d</xliff:g> direct messages.</string>