diff --git a/build.gradle b/build.gradle index 847705bf4..4710ce4e2 100644 --- a/build.gradle +++ b/build.gradle @@ -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 } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/app/TwidereApplication.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/app/TwidereApplication.kt index 630b36efe..a13b819af 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/app/TwidereApplication.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/app/TwidereApplication.kt @@ -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" diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/NotificationChannelSpecsExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/NotificationChannelSpecsExtensions.kt index 6318d4d25..451e6903f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/NotificationChannelSpecsExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/NotificationChannelSpecsExtensions.kt @@ -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()) } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/notification/NotificationChannelSpec.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/notification/NotificationChannelSpec.kt index ceaae31ba..89c15cc91 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/model/notification/NotificationChannelSpec.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/notification/NotificationChannelSpec.kt @@ -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) } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt index 2ca8f4ce2..84e9a90bc 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/ContentNotificationManager.kt @@ -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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/NotificationChannelsManager.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/NotificationChannelsManager.kt index e059a4d85..9915612d0 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/NotificationChannelsManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/notification/NotificationChannelsManager.kt @@ -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() + + 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() + val addedGroups = mutableListOf() + + 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) + } + } } } } \ No newline at end of file diff --git a/twidere/src/main/res/values/strings.xml b/twidere/src/main/res/values/strings.xml index 1a839df98..c80fffb83 100644 --- a/twidere/src/main/res/values/strings.xml +++ b/twidere/src/main/res/values/strings.xml @@ -774,11 +774,10 @@ Important messages like DMs App notices Background operations - Content interactions - Content messages - Content notices - Content subscriptions - Content updates + Interactions + Messages + Subscriptions + Updates Service statuses %s sent you a direct message. %1$s sent you %2$d direct messages.