1 /*
<lambda>null2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.statusbar.notification.row
18 
19 import android.animation.ArgbEvaluator
20 import android.animation.ValueAnimator
21 import android.app.NotificationChannel
22 import android.app.NotificationManager.IMPORTANCE_DEFAULT
23 import android.app.NotificationManager.IMPORTANCE_NONE
24 import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
25 import android.content.Context
26 import android.graphics.drawable.Drawable
27 import android.text.TextUtils
28 import android.transition.AutoTransition
29 import android.transition.Transition
30 import android.transition.TransitionManager
31 import android.util.AttributeSet
32 import android.view.LayoutInflater
33 import android.view.View
34 import android.widget.ImageView
35 import android.widget.LinearLayout
36 import android.widget.Switch
37 import android.widget.TextView
38 import com.android.settingslib.Utils
39 
40 import com.android.systemui.R
41 import com.android.systemui.util.Assert
42 
43 /**
44  * Half-shelf for notification channel controls
45  */
46 class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
47     lateinit var controller: ChannelEditorDialogController
48     var appIcon: Drawable? = null
49     var appName: String? = null
50     var channels = mutableListOf<NotificationChannel>()
51         set(newValue) {
52             field = newValue
53             updateRows()
54         }
55 
56     // The first row is for the entire app
57     private lateinit var appControlRow: AppControlView
58     private val channelRows = mutableListOf<ChannelRow>()
59 
60     override fun onFinishInflate() {
61         super.onFinishInflate()
62 
63         appControlRow = findViewById(R.id.app_control)
64     }
65 
66     /**
67      * Play a highlight animation for the given channel (it really should exist but this will just
68      * fail silently if it doesn't)
69      */
70     fun highlightChannel(channel: NotificationChannel) {
71         Assert.isMainThread()
72         for (row in channelRows) {
73             if (row.channel == channel) {
74                 row.playHighlight()
75             }
76         }
77     }
78 
79     private fun updateRows() {
80         val enabled = controller.areAppNotificationsEnabled()
81 
82         val transition = AutoTransition()
83         transition.duration = 200
84         transition.addListener(object : Transition.TransitionListener {
85             override fun onTransitionEnd(p0: Transition?) {
86                 notifySubtreeAccessibilityStateChangedIfNeeded()
87             }
88 
89             override fun onTransitionResume(p0: Transition?) {
90             }
91 
92             override fun onTransitionPause(p0: Transition?) {
93             }
94 
95             override fun onTransitionCancel(p0: Transition?) {
96             }
97 
98             override fun onTransitionStart(p0: Transition?) {
99             }
100         })
101         TransitionManager.beginDelayedTransition(this, transition)
102 
103         // Remove any rows
104         for (row in channelRows) {
105             removeView(row)
106         }
107         channelRows.clear()
108 
109         updateAppControlRow(enabled)
110 
111         if (enabled) {
112             val inflater = LayoutInflater.from(context)
113             for (channel in channels) {
114                 addChannelRow(channel, inflater)
115             }
116         }
117     }
118 
119     private fun addChannelRow(channel: NotificationChannel, inflater: LayoutInflater) {
120         val row = inflater.inflate(R.layout.notif_half_shelf_row, null) as ChannelRow
121         row.controller = controller
122         row.channel = channel
123 
124         channelRows.add(row)
125         addView(row)
126     }
127 
128     private fun updateAppControlRow(enabled: Boolean) {
129         appControlRow.iconView.setImageDrawable(appIcon)
130         appControlRow.channelName.text = context.resources
131                 .getString(R.string.notification_channel_dialog_title, appName)
132         appControlRow.switch.isChecked = enabled
133         appControlRow.switch.setOnCheckedChangeListener { _, b ->
134             controller.proposeSetAppNotificationsEnabled(b)
135             updateRows()
136         }
137     }
138 }
139 
140 class AppControlView(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
141     lateinit var iconView: ImageView
142     lateinit var channelName: TextView
143     lateinit var switch: Switch
144 
onFinishInflatenull145     override fun onFinishInflate() {
146         iconView = findViewById(R.id.icon)
147         channelName = findViewById(R.id.app_name)
148         switch = findViewById(R.id.toggle)
149 
150         setOnClickListener { switch.toggle() }
151     }
152 }
153 
154 class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
155 
156     lateinit var controller: ChannelEditorDialogController
157     private lateinit var channelName: TextView
158     private lateinit var channelDescription: TextView
159     private lateinit var switch: Switch
160     private val highlightColor: Int
161     var gentle = false
162 
163     init {
164         highlightColor = Utils.getColorAttrDefaultColor(
165                 context, android.R.attr.colorControlHighlight)
166     }
167 
168     var channel: NotificationChannel? = null
169         set(newValue) {
170             field = newValue
171             updateImportance()
172             updateViews()
173         }
174 
onFinishInflatenull175     override fun onFinishInflate() {
176         super.onFinishInflate()
177         channelName = findViewById(R.id.channel_name)
178         channelDescription = findViewById(R.id.channel_description)
179         switch = findViewById(R.id.toggle)
180         switch.setOnCheckedChangeListener { _, b ->
181             channel?.let {
182                 controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE)
183             }
184         }
185         setOnClickListener { switch.toggle() }
186     }
187 
188     /**
189      * Play an animation that highlights this row
190      */
playHighlightnull191     fun playHighlight() {
192         // Use 0 for the start value because our background is given to us by our parent
193         val fadeInLoop = ValueAnimator.ofObject(ArgbEvaluator(), 0, highlightColor)
194         fadeInLoop.duration = 200L
195         fadeInLoop.addUpdateListener { animator ->
196             setBackgroundColor(animator.animatedValue as Int)
197         }
198         fadeInLoop.repeatMode = ValueAnimator.REVERSE
199         // Repeat an odd number of times to we end up normal
200         fadeInLoop.repeatCount = 5
201         fadeInLoop.start()
202     }
203 
updateViewsnull204     private fun updateViews() {
205         val nc = channel ?: return
206 
207         channelName.text = nc.name ?: ""
208 
209         nc.group?.let { groupId ->
210             channelDescription.text = controller.groupNameForId(groupId)
211         }
212 
213         if (nc.group == null || TextUtils.isEmpty(channelDescription.text)) {
214             channelDescription.visibility = View.GONE
215         } else {
216             channelDescription.visibility = View.VISIBLE
217         }
218 
219         switch.isChecked = nc.importance != IMPORTANCE_NONE
220     }
221 
updateImportancenull222     private fun updateImportance() {
223         val importance = channel?.importance ?: 0
224         gentle = importance != IMPORTANCE_UNSPECIFIED && importance < IMPORTANCE_DEFAULT
225     }
226 }
227