Loading packages/SystemUI/src/com/android/systemui/Dependency.java +0 −3 Original line number Diff line number Diff line Loading @@ -76,7 +76,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager.Keyg import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; Loading Loading @@ -308,7 +307,6 @@ public class Dependency { @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper; @Inject Lazy<SensorPrivacyController> mSensorPrivacyController; @Inject Lazy<DockManager> mDockManager; @Inject Lazy<ChannelEditorDialogController> mChannelEditorDialogController; @Inject Lazy<INotificationManager> mINotificationManager; @Inject Lazy<SysUiState> mSysUiStateFlagsContainer; @Inject Lazy<AlarmManager> mAlarmManager; Loading Loading @@ -498,7 +496,6 @@ public class Dependency { mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get); mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get); mProviders.put(DockManager.class, mDockManager::get); mProviders.put(ChannelEditorDialogController.class, mChannelEditorDialogController::get); mProviders.put(INotificationManager.class, mINotificationManager::get); mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get); mProviders.put(AlarmManager.class, mAlarmManager::get); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +3 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController; Loading Loading @@ -109,6 +110,7 @@ public interface NotificationsModule { INotificationManager notificationManager, LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, CurrentUserContextTracker contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider) { return new NotificationGutsManager( Loading @@ -121,6 +123,7 @@ public interface NotificationsModule { notificationManager, launcherApps, shortcutManager, channelEditorDialogController, contextTracker, builderProvider); } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +86 −24 Original line number Diff line number Diff line Loading @@ -22,8 +22,8 @@ import android.app.NotificationChannel import android.app.NotificationChannel.DEFAULT_CHANNEL_ID import android.app.NotificationChannelGroup import android.app.NotificationManager.IMPORTANCE_NONE import android.app.NotificationManager.Importance import android.content.Context import android.content.DialogInterface import android.graphics.Color import android.graphics.PixelFormat import android.graphics.drawable.ColorDrawable Loading @@ -37,8 +37,10 @@ import android.view.Window import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R import javax.inject.Inject import javax.inject.Singleton Loading @@ -59,11 +61,13 @@ private const val TAG = "ChannelDialogController" @Singleton class ChannelEditorDialogController @Inject constructor( c: Context, private val noMan: INotificationManager private val noMan: INotificationManager, private val dialogBuilder: ChannelEditorDialog.Builder ) { val context: Context = c.applicationContext lateinit var dialog: Dialog private var prepared = false private lateinit var dialog: ChannelEditorDialog private var appIcon: Drawable? = null private var appUid: Int? = null Loading @@ -74,13 +78,16 @@ class ChannelEditorDialogController @Inject constructor( // Caller should set this if they care about when we dismiss var onFinishListener: OnChannelEditorDialogFinishedListener? = null // Channels handed to us from NotificationInfo @VisibleForTesting internal val providedChannels = mutableListOf<NotificationChannel>() internal val paddedChannels = mutableListOf<NotificationChannel>() // Channels handed to us from NotificationInfo private val providedChannels = mutableListOf<NotificationChannel>() // Map from NotificationChannel to importance private val edits = mutableMapOf<NotificationChannel, Int>() var appNotificationsEnabled = true private var appNotificationsEnabled = true // System settings for app notifications private var appNotificationsCurrentlyEnabled: Boolean? = null // Keep a mapping of NotificationChannel.getGroup() to the actual group name for display @VisibleForTesting Loading @@ -106,10 +113,18 @@ class ChannelEditorDialogController @Inject constructor( this.appNotificationsEnabled = checkAreAppNotificationsOn() this.onSettingsClickListener = onSettingsClickListener // These will always start out the same appNotificationsCurrentlyEnabled = appNotificationsEnabled channelGroupList.clear() channelGroupList.addAll(fetchNotificationChannelGroups()) buildGroupNameLookup() providedChannels.clear() providedChannels.addAll(channels) padToFourChannels(channels) initDialog() prepared = true } private fun buildGroupNameLookup() { Loading @@ -121,21 +136,21 @@ class ChannelEditorDialogController @Inject constructor( } private fun padToFourChannels(channels: Set<NotificationChannel>) { providedChannels.clear() paddedChannels.clear() // First, add all of the given channels providedChannels.addAll(channels.asSequence().take(4)) paddedChannels.addAll(channels.asSequence().take(4)) // Then pad to 4 if we haven't been given that many providedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence()) .filterNot { providedChannels.contains(it) } paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence()) .filterNot { paddedChannels.contains(it) } .distinct() .take(4 - providedChannels.size)) .take(4 - paddedChannels.size)) // If we only got one channel and it has the default miscellaneous tag, then we actually // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the // channel if (providedChannels.size == 1 && DEFAULT_CHANNEL_ID == providedChannels[0].id) { providedChannels.clear() if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) { paddedChannels.clear() } } Loading @@ -157,7 +172,9 @@ class ChannelEditorDialogController @Inject constructor( } fun show() { initDialog() if (!prepared) { throw IllegalStateException("Must call prepareDialogForApp() before calling show()") } dialog.show() } Loading @@ -178,8 +195,10 @@ class ChannelEditorDialogController @Inject constructor( appUid = null packageName = null appName = null appNotificationsCurrentlyEnabled = null edits.clear() paddedChannels.clear() providedChannels.clear() groupNameLookup.clear() } Loading @@ -188,12 +207,27 @@ class ChannelEditorDialogController @Inject constructor( return groupNameLookup[groupId] ?: "" } fun proposeEditForChannel(channel: NotificationChannel, edit: Int) { fun proposeEditForChannel(channel: NotificationChannel, @Importance edit: Int) { if (channel.importance == edit) { edits.remove(channel) } else { edits[channel] = edit } dialog.updateDoneButtonText(hasChanges()) } fun proposeSetAppNotificationsEnabled(enabled: Boolean) { appNotificationsEnabled = enabled dialog.updateDoneButtonText(hasChanges()) } fun areAppNotificationsEnabled(): Boolean { return appNotificationsEnabled } private fun hasChanges(): Boolean { return edits.isNotEmpty() || (appNotificationsEnabled != appNotificationsCurrentlyEnabled) } @Suppress("unchecked_cast") Loading Loading @@ -241,7 +275,7 @@ class ChannelEditorDialogController @Inject constructor( } } if (appNotificationsEnabled != checkAreAppNotificationsOn()) { if (appNotificationsEnabled != appNotificationsCurrentlyEnabled) { applyAppNotificationsOn(appNotificationsEnabled) } } Loading @@ -252,7 +286,8 @@ class ChannelEditorDialogController @Inject constructor( } private fun initDialog() { dialog = Dialog(context) dialogBuilder.setContext(context) dialog = dialogBuilder.build() dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) // Prevent a11y readers from reading the first element in the dialog twice Loading @@ -260,16 +295,21 @@ class ChannelEditorDialogController @Inject constructor( dialog.apply { setContentView(R.layout.notif_half_shelf) setCanceledOnTouchOutside(true) setOnDismissListener(object : DialogInterface.OnDismissListener { override fun onDismiss(dialog: DialogInterface?) { onFinishListener?.onChannelEditorDialogFinished() } }) findViewById<ChannelEditorListView>(R.id.half_shelf_container).apply { setOnDismissListener { onFinishListener?.onChannelEditorDialogFinished() } val listView = findViewById<ChannelEditorListView>(R.id.half_shelf_container) listView?.apply { controller = this@ChannelEditorDialogController appIcon = this@ChannelEditorDialogController.appIcon appName = this@ChannelEditorDialogController.appName channels = providedChannels channels = paddedChannels } setOnShowListener { // play a highlight animation for the given channels for (channel in providedChannels) { listView?.highlightChannel(channel) } } findViewById<TextView>(R.id.done_button)?.setOnClickListener { Loading Loading @@ -306,6 +346,28 @@ class ChannelEditorDialogController @Inject constructor( or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) } class ChannelEditorDialog(context: Context) : Dialog(context) { fun updateDoneButtonText(hasChanges: Boolean) { findViewById<TextView>(R.id.done_button)?.setText( if (hasChanges) R.string.inline_ok_button else R.string.inline_done_button) } class Builder @Inject constructor() { private lateinit var context: Context fun setContext(context: Context): Builder { this.context = context return this } fun build(): ChannelEditorDialog { return ChannelEditorDialog(context) } } } interface OnChannelEditorDialogFinishedListener { fun onChannelEditorDialogFinished() } packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt +48 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.row import android.animation.ArgbEvaluator import android.animation.ValueAnimator import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_NONE Loading @@ -33,8 +35,10 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.Switch import android.widget.TextView import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.util.Assert /** * Half-shelf for notification channel controls Loading @@ -51,6 +55,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a // The first row is for the entire app private lateinit var appControlRow: AppControlView private val channelRows = mutableListOf<ChannelRow>() override fun onFinishInflate() { super.onFinishInflate() Loading @@ -58,8 +63,21 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a appControlRow = findViewById(R.id.app_control) } /** * Play a highlight animation for the given channel (it really should exist but this will just * fail silently if it doesn't) */ fun highlightChannel(channel: NotificationChannel) { Assert.isMainThread() for (row in channelRows) { if (row.channel == channel) { row.playHighlight() } } } private fun updateRows() { val enabled = controller.appNotificationsEnabled val enabled = controller.areAppNotificationsEnabled() val transition = AutoTransition() transition.duration = 200 Loading @@ -83,13 +101,10 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a TransitionManager.beginDelayedTransition(this, transition) // Remove any rows val n = childCount for (i in n.downTo(0)) { val child = getChildAt(i) if (child is ChannelRow) { removeView(child) } for (row in channelRows) { removeView(row) } channelRows.clear() updateAppControlRow(enabled) Loading @@ -105,6 +120,8 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a val row = inflater.inflate(R.layout.notif_half_shelf_row, null) as ChannelRow row.controller = controller row.channel = channel channelRows.add(row) addView(row) } Loading @@ -114,7 +131,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a .getString(R.string.notification_channel_dialog_title, appName) appControlRow.switch.isChecked = enabled appControlRow.switch.setOnCheckedChangeListener { _, b -> controller.appNotificationsEnabled = b controller.proposeSetAppNotificationsEnabled(b) updateRows() } } Loading @@ -140,8 +157,14 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { private lateinit var channelName: TextView private lateinit var channelDescription: TextView private lateinit var switch: Switch private val highlightColor: Int var gentle = false init { highlightColor = Utils.getColorAttrDefaultColor( context, android.R.attr.colorControlHighlight) } var channel: NotificationChannel? = null set(newValue) { field = newValue Loading @@ -150,6 +173,7 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { } override fun onFinishInflate() { super.onFinishInflate() channelName = findViewById(R.id.channel_name) channelDescription = findViewById(R.id.channel_description) switch = findViewById(R.id.toggle) Loading @@ -161,6 +185,22 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { setOnClickListener { switch.toggle() } } /** * Play an animation that highlights this row */ fun playHighlight() { // Use 0 for the start value because our background is given to us by our parent val fadeInLoop = ValueAnimator.ofObject(ArgbEvaluator(), 0, highlightColor) fadeInLoop.duration = 200L fadeInLoop.addUpdateListener { animator -> setBackgroundColor(animator.animatedValue as Int) } fadeInLoop.repeatMode = ValueAnimator.REVERSE // Repeat an odd number of times to we end up normal fadeInLoop.repeatCount = 5 fadeInLoop.start() } private fun updateViews() { val nc = channel ?: return Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +5 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final VisualStabilityManager mVisualStabilityManager; private final AccessibilityManager mAccessibilityManager; private final HighPriorityProvider mHighPriorityProvider; private final ChannelEditorDialogController mChannelEditorDialogController; // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager = Loading Loading @@ -127,6 +128,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx INotificationManager notificationManager, LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, CurrentUserContextTracker contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider) { mContext = context; Loading @@ -140,6 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mShortcutManager = shortcutManager; mContextTracker = contextTracker; mBuilderProvider = builderProvider; mChannelEditorDialogController = channelEditorDialogController; } public void setUpWithPresenter(NotificationPresenter presenter, Loading Loading @@ -348,6 +351,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx pmUser, mNotificationManager, mVisualStabilityManager, mChannelEditorDialogController, packageName, row.getEntry().getChannel(), row.getUniqueChannels(), Loading Loading @@ -390,6 +394,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.bindNotification( pmUser, mNotificationManager, mChannelEditorDialogController, packageName, row.getEntry().getChannel(), row.getUniqueChannels(), Loading Loading
packages/SystemUI/src/com/android/systemui/Dependency.java +0 −3 Original line number Diff line number Diff line Loading @@ -76,7 +76,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager.Keyg import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; Loading Loading @@ -308,7 +307,6 @@ public class Dependency { @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper; @Inject Lazy<SensorPrivacyController> mSensorPrivacyController; @Inject Lazy<DockManager> mDockManager; @Inject Lazy<ChannelEditorDialogController> mChannelEditorDialogController; @Inject Lazy<INotificationManager> mINotificationManager; @Inject Lazy<SysUiState> mSysUiStateFlagsContainer; @Inject Lazy<AlarmManager> mAlarmManager; Loading Loading @@ -498,7 +496,6 @@ public class Dependency { mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get); mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get); mProviders.put(DockManager.class, mDockManager::get); mProviders.put(ChannelEditorDialogController.class, mChannelEditorDialogController::get); mProviders.put(INotificationManager.class, mINotificationManager::get); mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get); mProviders.put(AlarmManager.class, mAlarmManager::get); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +3 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController; Loading Loading @@ -109,6 +110,7 @@ public interface NotificationsModule { INotificationManager notificationManager, LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, CurrentUserContextTracker contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider) { return new NotificationGutsManager( Loading @@ -121,6 +123,7 @@ public interface NotificationsModule { notificationManager, launcherApps, shortcutManager, channelEditorDialogController, contextTracker, builderProvider); } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +86 −24 Original line number Diff line number Diff line Loading @@ -22,8 +22,8 @@ import android.app.NotificationChannel import android.app.NotificationChannel.DEFAULT_CHANNEL_ID import android.app.NotificationChannelGroup import android.app.NotificationManager.IMPORTANCE_NONE import android.app.NotificationManager.Importance import android.content.Context import android.content.DialogInterface import android.graphics.Color import android.graphics.PixelFormat import android.graphics.drawable.ColorDrawable Loading @@ -37,8 +37,10 @@ import android.view.Window import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R import javax.inject.Inject import javax.inject.Singleton Loading @@ -59,11 +61,13 @@ private const val TAG = "ChannelDialogController" @Singleton class ChannelEditorDialogController @Inject constructor( c: Context, private val noMan: INotificationManager private val noMan: INotificationManager, private val dialogBuilder: ChannelEditorDialog.Builder ) { val context: Context = c.applicationContext lateinit var dialog: Dialog private var prepared = false private lateinit var dialog: ChannelEditorDialog private var appIcon: Drawable? = null private var appUid: Int? = null Loading @@ -74,13 +78,16 @@ class ChannelEditorDialogController @Inject constructor( // Caller should set this if they care about when we dismiss var onFinishListener: OnChannelEditorDialogFinishedListener? = null // Channels handed to us from NotificationInfo @VisibleForTesting internal val providedChannels = mutableListOf<NotificationChannel>() internal val paddedChannels = mutableListOf<NotificationChannel>() // Channels handed to us from NotificationInfo private val providedChannels = mutableListOf<NotificationChannel>() // Map from NotificationChannel to importance private val edits = mutableMapOf<NotificationChannel, Int>() var appNotificationsEnabled = true private var appNotificationsEnabled = true // System settings for app notifications private var appNotificationsCurrentlyEnabled: Boolean? = null // Keep a mapping of NotificationChannel.getGroup() to the actual group name for display @VisibleForTesting Loading @@ -106,10 +113,18 @@ class ChannelEditorDialogController @Inject constructor( this.appNotificationsEnabled = checkAreAppNotificationsOn() this.onSettingsClickListener = onSettingsClickListener // These will always start out the same appNotificationsCurrentlyEnabled = appNotificationsEnabled channelGroupList.clear() channelGroupList.addAll(fetchNotificationChannelGroups()) buildGroupNameLookup() providedChannels.clear() providedChannels.addAll(channels) padToFourChannels(channels) initDialog() prepared = true } private fun buildGroupNameLookup() { Loading @@ -121,21 +136,21 @@ class ChannelEditorDialogController @Inject constructor( } private fun padToFourChannels(channels: Set<NotificationChannel>) { providedChannels.clear() paddedChannels.clear() // First, add all of the given channels providedChannels.addAll(channels.asSequence().take(4)) paddedChannels.addAll(channels.asSequence().take(4)) // Then pad to 4 if we haven't been given that many providedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence()) .filterNot { providedChannels.contains(it) } paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence()) .filterNot { paddedChannels.contains(it) } .distinct() .take(4 - providedChannels.size)) .take(4 - paddedChannels.size)) // If we only got one channel and it has the default miscellaneous tag, then we actually // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the // channel if (providedChannels.size == 1 && DEFAULT_CHANNEL_ID == providedChannels[0].id) { providedChannels.clear() if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) { paddedChannels.clear() } } Loading @@ -157,7 +172,9 @@ class ChannelEditorDialogController @Inject constructor( } fun show() { initDialog() if (!prepared) { throw IllegalStateException("Must call prepareDialogForApp() before calling show()") } dialog.show() } Loading @@ -178,8 +195,10 @@ class ChannelEditorDialogController @Inject constructor( appUid = null packageName = null appName = null appNotificationsCurrentlyEnabled = null edits.clear() paddedChannels.clear() providedChannels.clear() groupNameLookup.clear() } Loading @@ -188,12 +207,27 @@ class ChannelEditorDialogController @Inject constructor( return groupNameLookup[groupId] ?: "" } fun proposeEditForChannel(channel: NotificationChannel, edit: Int) { fun proposeEditForChannel(channel: NotificationChannel, @Importance edit: Int) { if (channel.importance == edit) { edits.remove(channel) } else { edits[channel] = edit } dialog.updateDoneButtonText(hasChanges()) } fun proposeSetAppNotificationsEnabled(enabled: Boolean) { appNotificationsEnabled = enabled dialog.updateDoneButtonText(hasChanges()) } fun areAppNotificationsEnabled(): Boolean { return appNotificationsEnabled } private fun hasChanges(): Boolean { return edits.isNotEmpty() || (appNotificationsEnabled != appNotificationsCurrentlyEnabled) } @Suppress("unchecked_cast") Loading Loading @@ -241,7 +275,7 @@ class ChannelEditorDialogController @Inject constructor( } } if (appNotificationsEnabled != checkAreAppNotificationsOn()) { if (appNotificationsEnabled != appNotificationsCurrentlyEnabled) { applyAppNotificationsOn(appNotificationsEnabled) } } Loading @@ -252,7 +286,8 @@ class ChannelEditorDialogController @Inject constructor( } private fun initDialog() { dialog = Dialog(context) dialogBuilder.setContext(context) dialog = dialogBuilder.build() dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) // Prevent a11y readers from reading the first element in the dialog twice Loading @@ -260,16 +295,21 @@ class ChannelEditorDialogController @Inject constructor( dialog.apply { setContentView(R.layout.notif_half_shelf) setCanceledOnTouchOutside(true) setOnDismissListener(object : DialogInterface.OnDismissListener { override fun onDismiss(dialog: DialogInterface?) { onFinishListener?.onChannelEditorDialogFinished() } }) findViewById<ChannelEditorListView>(R.id.half_shelf_container).apply { setOnDismissListener { onFinishListener?.onChannelEditorDialogFinished() } val listView = findViewById<ChannelEditorListView>(R.id.half_shelf_container) listView?.apply { controller = this@ChannelEditorDialogController appIcon = this@ChannelEditorDialogController.appIcon appName = this@ChannelEditorDialogController.appName channels = providedChannels channels = paddedChannels } setOnShowListener { // play a highlight animation for the given channels for (channel in providedChannels) { listView?.highlightChannel(channel) } } findViewById<TextView>(R.id.done_button)?.setOnClickListener { Loading Loading @@ -306,6 +346,28 @@ class ChannelEditorDialogController @Inject constructor( or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) } class ChannelEditorDialog(context: Context) : Dialog(context) { fun updateDoneButtonText(hasChanges: Boolean) { findViewById<TextView>(R.id.done_button)?.setText( if (hasChanges) R.string.inline_ok_button else R.string.inline_done_button) } class Builder @Inject constructor() { private lateinit var context: Context fun setContext(context: Context): Builder { this.context = context return this } fun build(): ChannelEditorDialog { return ChannelEditorDialog(context) } } } interface OnChannelEditorDialogFinishedListener { fun onChannelEditorDialogFinished() }
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt +48 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.row import android.animation.ArgbEvaluator import android.animation.ValueAnimator import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_NONE Loading @@ -33,8 +35,10 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.Switch import android.widget.TextView import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.util.Assert /** * Half-shelf for notification channel controls Loading @@ -51,6 +55,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a // The first row is for the entire app private lateinit var appControlRow: AppControlView private val channelRows = mutableListOf<ChannelRow>() override fun onFinishInflate() { super.onFinishInflate() Loading @@ -58,8 +63,21 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a appControlRow = findViewById(R.id.app_control) } /** * Play a highlight animation for the given channel (it really should exist but this will just * fail silently if it doesn't) */ fun highlightChannel(channel: NotificationChannel) { Assert.isMainThread() for (row in channelRows) { if (row.channel == channel) { row.playHighlight() } } } private fun updateRows() { val enabled = controller.appNotificationsEnabled val enabled = controller.areAppNotificationsEnabled() val transition = AutoTransition() transition.duration = 200 Loading @@ -83,13 +101,10 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a TransitionManager.beginDelayedTransition(this, transition) // Remove any rows val n = childCount for (i in n.downTo(0)) { val child = getChildAt(i) if (child is ChannelRow) { removeView(child) } for (row in channelRows) { removeView(row) } channelRows.clear() updateAppControlRow(enabled) Loading @@ -105,6 +120,8 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a val row = inflater.inflate(R.layout.notif_half_shelf_row, null) as ChannelRow row.controller = controller row.channel = channel channelRows.add(row) addView(row) } Loading @@ -114,7 +131,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a .getString(R.string.notification_channel_dialog_title, appName) appControlRow.switch.isChecked = enabled appControlRow.switch.setOnCheckedChangeListener { _, b -> controller.appNotificationsEnabled = b controller.proposeSetAppNotificationsEnabled(b) updateRows() } } Loading @@ -140,8 +157,14 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { private lateinit var channelName: TextView private lateinit var channelDescription: TextView private lateinit var switch: Switch private val highlightColor: Int var gentle = false init { highlightColor = Utils.getColorAttrDefaultColor( context, android.R.attr.colorControlHighlight) } var channel: NotificationChannel? = null set(newValue) { field = newValue Loading @@ -150,6 +173,7 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { } override fun onFinishInflate() { super.onFinishInflate() channelName = findViewById(R.id.channel_name) channelDescription = findViewById(R.id.channel_description) switch = findViewById(R.id.toggle) Loading @@ -161,6 +185,22 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) { setOnClickListener { switch.toggle() } } /** * Play an animation that highlights this row */ fun playHighlight() { // Use 0 for the start value because our background is given to us by our parent val fadeInLoop = ValueAnimator.ofObject(ArgbEvaluator(), 0, highlightColor) fadeInLoop.duration = 200L fadeInLoop.addUpdateListener { animator -> setBackgroundColor(animator.animatedValue as Int) } fadeInLoop.repeatMode = ValueAnimator.REVERSE // Repeat an odd number of times to we end up normal fadeInLoop.repeatCount = 5 fadeInLoop.start() } private fun updateViews() { val nc = channel ?: return Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +5 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final VisualStabilityManager mVisualStabilityManager; private final AccessibilityManager mAccessibilityManager; private final HighPriorityProvider mHighPriorityProvider; private final ChannelEditorDialogController mChannelEditorDialogController; // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager = Loading Loading @@ -127,6 +128,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx INotificationManager notificationManager, LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, CurrentUserContextTracker contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider) { mContext = context; Loading @@ -140,6 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mShortcutManager = shortcutManager; mContextTracker = contextTracker; mBuilderProvider = builderProvider; mChannelEditorDialogController = channelEditorDialogController; } public void setUpWithPresenter(NotificationPresenter presenter, Loading Loading @@ -348,6 +351,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx pmUser, mNotificationManager, mVisualStabilityManager, mChannelEditorDialogController, packageName, row.getEntry().getChannel(), row.getUniqueChannels(), Loading Loading @@ -390,6 +394,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.bindNotification( pmUser, mNotificationManager, mChannelEditorDialogController, packageName, row.getEntry().getChannel(), row.getUniqueChannels(), Loading