Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0fd06541 authored by Evan Laird's avatar Evan Laird
Browse files

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
parent 18bd6e67
Loading
Loading
Loading
Loading
+25 −17
Original line number Diff line number Diff line
@@ -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 {
+45 −6
Original line number Diff line number Diff line
@@ -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

+3 −3
Original line number Diff line number Diff line
@@ -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