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