Highlight provided channels
The ChannelEditorDialogController is handed up to 4 channels from NotificationInfo when presented, and pads out to 4 in the case when it gets fewer than 4 (the most common case is that it gets 1). This change tells the controller to play a highlight animation for the provided channel(s) so that it's more obvious to users which channel they are editing the preference of. Test: atest SystemUITests; manual Bug: 140956160 Change-Id: I48717c7ca6e54259e54f32a5af6acc4c264cc8eb
This commit is contained in:
@@ -24,7 +24,6 @@ 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
|
||||
@@ -79,9 +78,10 @@ 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>()
|
||||
@@ -119,6 +119,8 @@ class ChannelEditorDialogController @Inject constructor(
|
||||
channelGroupList.clear()
|
||||
channelGroupList.addAll(fetchNotificationChannelGroups())
|
||||
buildGroupNameLookup()
|
||||
providedChannels.clear()
|
||||
providedChannels.addAll(channels)
|
||||
padToFourChannels(channels)
|
||||
initDialog()
|
||||
|
||||
@@ -134,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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +198,7 @@ class ChannelEditorDialogController @Inject constructor(
|
||||
appNotificationsCurrentlyEnabled = null
|
||||
|
||||
edits.clear()
|
||||
paddedChannels.clear()
|
||||
providedChannels.clear()
|
||||
groupNameLookup.clear()
|
||||
}
|
||||
@@ -292,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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -58,6 +63,19 @@ 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.areAppNotificationsEnabled()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -162,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
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
|
||||
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
|
||||
setOf(channel1, channel2), appIcon, clickListener)
|
||||
|
||||
assertEquals(2, controller.providedChannels.size)
|
||||
assertEquals(2, controller.paddedChannels.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,7 +104,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
|
||||
setOf(channelDefault), appIcon, clickListener)
|
||||
|
||||
assertEquals("No channels should be shown when there is only the miscellaneous channel",
|
||||
0, controller.providedChannels.size)
|
||||
0, controller.paddedChannels.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,7 +126,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
|
||||
setOf(channel1), appIcon, clickListener)
|
||||
|
||||
assertEquals("ChannelEditorDialog should fetch enough channels to show 4",
|
||||
4, controller.providedChannels.size)
|
||||
4, controller.paddedChannels.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user