1 /*
2  * Copyright (C) 2021 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.car.statusicon;
18 
19 import android.annotation.DimenRes;
20 import android.annotation.LayoutRes;
21 import android.graphics.drawable.Drawable;
22 import android.view.View;
23 import android.widget.ImageView;
24 
25 import androidx.annotation.VisibleForTesting;
26 import androidx.lifecycle.MutableLiveData;
27 import androidx.lifecycle.Observer;
28 
29 import com.android.systemui.R;
30 
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /**
35  * Abstract class to extend to control views that display a certain status icon.
36  */
37 public abstract class StatusIconController {
38     public static final int PANEL_CONTENT_LAYOUT_NONE = -1;
39 
40     private final StatusIconData mStatusIconData = new StatusIconData();
41     private final MutableLiveData<StatusIconData> mStatusIconLiveData =
42             new MutableLiveData<>(mStatusIconData);
43     private final Map<ImageView, Observer<StatusIconData>> mObserverMap = new HashMap<>();
44 
45     /**
46      * Interface definition for a callback to be invoked when a status icon is updated.
47      */
48     public interface OnStatusUpdatedListener {
49         /**
50          * Reports that a status icon is updated.
51          *
52          * @param statusIconController controller to display a certain status icon.
53          */
onStatusUpdated(StatusIconController statusIconController)54         void onStatusUpdated(StatusIconController statusIconController);
55     }
56 
57     private OnStatusUpdatedListener mOnStatusUpdatedListener;
58 
setOnStatusUpdatedListener(OnStatusUpdatedListener l)59     public void setOnStatusUpdatedListener(OnStatusUpdatedListener l) {
60         mOnStatusUpdatedListener = l;
61     }
62 
63     /**
64      * Registers an {@link ImageView} to contain the icon that this controller controls.
65      */
registerIconView(ImageView view)66     public final void registerIconView(ImageView view) {
67         if (mObserverMap.containsKey(view)) return;
68 
69         Observer<StatusIconData> observer = statusIconData -> updateIconView(view, statusIconData);
70         mObserverMap.put(view, observer);
71         mStatusIconLiveData.observeForever(observer);
72     }
73 
74     /**
75      * Unregisters the observer for an {@link ImageView}.
76      */
unregisterIconView(ImageView view)77     public final void unregisterIconView(ImageView view) {
78         Observer<StatusIconData> observer = mObserverMap.remove(view);
79         if (observer != null) {
80             mStatusIconLiveData.removeObserver(observer);
81         }
82     }
83 
84     /**
85      * Returns the {@link Drawable} set to be displayed as the icon.
86      */
getIconDrawableToDisplay()87     public Drawable getIconDrawableToDisplay() {
88         return mStatusIconData.getIconDrawable();
89     }
90 
91     /**
92      * Sets the icon drawable to display.
93      */
setIconContentDescription(String str)94     protected final void setIconContentDescription(String str) {
95         mStatusIconData.setContentDescription(str);
96     }
97 
98     /**
99      * Sets the icon drawable to display.
100      */
setIconDrawableToDisplay(Drawable drawable)101     protected final void setIconDrawableToDisplay(Drawable drawable) {
102         mStatusIconData.setIconDrawable(drawable);
103     }
104 
105     /**
106      * Sets the icon visibility.
107      *
108      * NOTE: Icons are visible by default.
109      */
setIconVisibility(boolean isVisible)110     protected final void setIconVisibility(boolean isVisible) {
111         mStatusIconData.setIsIconVisible(isVisible);
112     }
113 
114     /**
115      * Lifecycle method executed when this controller is destroyed to clean up any references.
116      */
onDestroy()117     protected void onDestroy() {
118     }
119 
120     /**
121      * Provides observing views with the {@link StatusIconData} and causes them to update
122      * themselves accordingly through {@link #updateIconView}.
123      */
onStatusUpdated()124     protected void onStatusUpdated() {
125         mStatusIconLiveData.setValue(mStatusIconData);
126         if (mOnStatusUpdatedListener != null) {
127             mOnStatusUpdatedListener.onStatusUpdated(this);
128         }
129     }
130 
131     /**
132      * Updates the icon view based on the current {@link StatusIconData}.
133      */
updateIconView(ImageView view, StatusIconData data)134     protected void updateIconView(ImageView view, StatusIconData data) {
135         view.setImageDrawable(data.getIconDrawable());
136         view.setVisibility(data.getIsIconVisible() ? View.VISIBLE : View.GONE);
137         view.setContentDescription(data.getContentDescription());
138     }
139 
140     /**
141      * Returns the resource id of the layout to be drawn inside the panel associated with this
142      * status icon.
143      *
144      * By default, {@link #PANEL_CONTENT_LAYOUT_NONE} is returned and no panel will be attached to
145      * the status icon.
146      */
147     @LayoutRes
getPanelContentLayout()148     protected int getPanelContentLayout() {
149         return PANEL_CONTENT_LAYOUT_NONE;
150     }
151 
152     /**
153      * Returns the resource id of the width for the panel associated with this status icon.
154      */
155     @DimenRes
getPanelWidth()156     protected int getPanelWidth() {
157         return R.dimen.car_status_icon_panel_default_width;
158     }
159 
160     /**
161      * Determines the icon to display via {@link #setIconDrawableToDisplay} and notifies observing
162      * views by calling {@link #onStatusUpdated} at the end.
163      */
updateStatus()164     protected abstract void updateStatus();
165 
166     /**
167      * Gets the Id for the View.
168      */
getId()169     protected abstract int getId();
170 
171     @VisibleForTesting
isViewRegistered(ImageView view)172     boolean isViewRegistered(ImageView view) {
173         return mObserverMap.containsKey(view);
174     }
175 }
176