1 /*
2  * Copyright (C) 2017 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 package com.android.systemui.statusbar.phone
17 
18 import android.annotation.IntDef
19 import android.content.Context
20 import android.graphics.drawable.Icon
21 import android.os.UserHandle
22 import com.android.internal.statusbar.StatusBarIcon
23 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState
24 import com.android.systemui.statusbar.pipeline.icons.shared.model.ModernStatusBarViewCreator
25 
26 /** Wraps [com.android.internal.statusbar.StatusBarIcon] so we can still have a uniform list */
27 open class StatusBarIconHolder private constructor() {
28     @IntDef(TYPE_ICON, TYPE_MOBILE_NEW, TYPE_WIFI_NEW, TYPE_BINDABLE)
29     @Retention(AnnotationRetention.SOURCE)
30     internal annotation class IconType
31 
32     var icon: StatusBarIcon? = null
33 
34     @IconType
35     open var type = TYPE_ICON
36         internal set
37 
38     var tag = 0
39         private set
40 
41     open var isVisible: Boolean
42         get() =
43             when (type) {
44                 TYPE_ICON -> icon!!.visible
45 
46                 // The new pipeline controls visibilities via the view model and
47                 // view binder, so
48                 // this is effectively an unused return value.
49                 TYPE_BINDABLE,
50                 TYPE_MOBILE_NEW,
51                 TYPE_WIFI_NEW -> true
52                 else -> true
53             }
54         set(visible) {
55             if (isVisible == visible) {
56                 return
57             }
58             when (type) {
59                 TYPE_ICON -> icon!!.visible = visible
60                 TYPE_BINDABLE,
61                 TYPE_MOBILE_NEW,
62                 TYPE_WIFI_NEW -> {}
63             }
64         }
65 
toStringnull66     override fun toString(): String {
67         return ("StatusBarIconHolder(type=${getTypeString(type)}" +
68             " tag=$tag" +
69             " visible=$isVisible)")
70     }
71 
72     companion object {
73         const val TYPE_ICON = 0
74 
75         /**
76          * TODO (b/249790733): address this once the new pipeline is in place This type exists so
77          * that the new pipeline (see [MobileIconViewModel]) can be used to inform the old view
78          * system about changes to the data set (the list of mobile icons). The design of the new
79          * pipeline should allow for removal of this icon holder type, and obsolete the need for
80          * this entire class.
81          */
82         @Deprecated(
83             """This field only exists so the new status bar pipeline can interface with the
84       view holder system."""
85         )
86         const val TYPE_MOBILE_NEW = 3
87 
88         /**
89          * TODO (b/238425913): address this once the new pipeline is in place This type exists so
90          * that the new wifi pipeline can be used to inform the old view system about the existence
91          * of the wifi icon. The design of the new pipeline should allow for removal of this icon
92          * holder type, and obsolete the need for this entire class.
93          */
94         @Deprecated(
95             """This field only exists so the new status bar pipeline can interface with the
96       view holder system."""
97         )
98         const val TYPE_WIFI_NEW = 4
99 
100         /** Only applicable to [BindableIconHolder] */
101         const val TYPE_BINDABLE = 5
102 
103         /** Returns a human-readable string representing the given type. */
getTypeStringnull104         fun getTypeString(@IconType type: Int): String {
105             return when (type) {
106                 TYPE_ICON -> "ICON"
107                 TYPE_MOBILE_NEW -> "MOBILE_NEW"
108                 TYPE_WIFI_NEW -> "WIFI_NEW"
109                 else -> "UNKNOWN"
110             }
111         }
112 
113         @JvmStatic
fromIconnull114         fun fromIcon(icon: StatusBarIcon?): StatusBarIconHolder {
115             val wrapper = StatusBarIconHolder()
116             wrapper.icon = icon
117             return wrapper
118         }
119 
120         /** Creates a new holder with for the new wifi icon. */
121         @JvmStatic
forNewWifiIconnull122         fun forNewWifiIcon(): StatusBarIconHolder {
123             val holder = StatusBarIconHolder()
124             holder.type = TYPE_WIFI_NEW
125             return holder
126         }
127 
128         /**
129          * ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
130          * determine icon ordering and building the correct view model
131          */
132         @JvmStatic
fromSubIdForModernMobileIconnull133         fun fromSubIdForModernMobileIcon(subId: Int): StatusBarIconHolder {
134             val holder = StatusBarIconHolder()
135             holder.type = TYPE_MOBILE_NEW
136             holder.tag = subId
137             return holder
138         }
139 
140         /** Creates a new StatusBarIconHolder from a CallIndicatorIconState. */
141         @JvmStatic
fromCallIndicatorStatenull142         fun fromCallIndicatorState(
143             context: Context,
144             state: CallIndicatorIconState,
145         ): StatusBarIconHolder {
146             val holder = StatusBarIconHolder()
147             val resId = if (state.isNoCalling) state.noCallingResId else state.callStrengthResId
148             val contentDescription =
149                 if (state.isNoCalling) state.noCallingDescription else state.callStrengthDescription
150             holder.icon =
151                 StatusBarIcon(
152                     UserHandle.SYSTEM,
153                     context.packageName,
154                     Icon.createWithResource(context, resId),
155                     0,
156                     0,
157                     contentDescription,
158                     StatusBarIcon.Type.SystemIcon,
159                 )
160             holder.tag = state.subId
161             return holder
162         }
163     }
164 
165     /**
166      * Subclass of StatusBarIconHolder that is responsible only for the registration of an icon into
167      * the [StatusBarIconList]. A bindable icon takes care of its own display, including hiding
168      * itself under the correct conditions.
169      *
170      * StatusBarIconController will register all available bindable icons on init (see
171      * [BindableIconsRepository]), and will ignore any call to setIcon for these.
172      *
173      * @property initializer a view creator that can bind the relevant view models to the created
174      *   view.
175      * @property slot the name of the slot that this holder is used for.
176      */
177     class BindableIconHolder(val initializer: ModernStatusBarViewCreator, val slot: String) :
178         StatusBarIconHolder() {
179         override var type: Int = TYPE_BINDABLE
180 
181         /** This is unused, as bindable icons use their own view binders to control visibility */
182         override var isVisible: Boolean = true
183 
toStringnull184         override fun toString(): String {
185             return ("StatusBarIconHolder(type=BINDABLE, slot=$slot)")
186         }
187     }
188 }
189