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 static com.android.systemui.car.statusicon.StatusIconController.PANEL_CONTENT_LAYOUT_NONE;
20 
21 import android.annotation.ArrayRes;
22 import android.annotation.ColorInt;
23 import android.annotation.LayoutRes;
24 import android.content.Context;
25 import android.content.res.Resources;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.ImageView;
30 
31 import com.android.systemui.R;
32 import com.android.systemui.broadcast.BroadcastDispatcher;
33 import com.android.systemui.car.CarServiceProvider;
34 import com.android.systemui.dagger.qualifiers.Main;
35 import com.android.systemui.statusbar.policy.ConfigurationController;
36 
37 import java.lang.reflect.Constructor;
38 import java.lang.reflect.InvocationTargetException;
39 import java.util.Map;
40 
41 import javax.inject.Provider;
42 
43 /**
44  * A base controller for a view that contains a group of StatusIcons. It creates a button view for
45  * each icon to display, instantiates {@link StatusIconController} instances associated with those
46  * icons, and then registers those icons to those controllers.
47  */
48 public abstract class StatusIconGroupContainerController {
49     private final Context mContext;
50     private final Resources mResources;
51     private final CarServiceProvider mCarServiceProvider;
52     private final BroadcastDispatcher mBroadcastDispatcher;
53     private final ConfigurationController mConfigurationController;
54     private final Map<Class<?>, Provider<StatusIconController>> mIconControllerCreators;
55     private final String mIconTag;
56     private final @ColorInt int mIconNotHighlightedColor;
57     private final String[] mStatusIconControllerNames;
58 
StatusIconGroupContainerController( Context context, @Main Resources resources, CarServiceProvider carServiceProvider, BroadcastDispatcher broadcastDispatcher, ConfigurationController configurationController, Map<Class<?>, Provider<StatusIconController>> iconControllerCreators)59     public StatusIconGroupContainerController(
60             Context context,
61             @Main Resources resources,
62             CarServiceProvider carServiceProvider,
63             BroadcastDispatcher broadcastDispatcher,
64             ConfigurationController configurationController,
65             Map<Class<?>, Provider<StatusIconController>> iconControllerCreators) {
66         mContext = context;
67         mResources = resources;
68         mCarServiceProvider = carServiceProvider;
69         mBroadcastDispatcher = broadcastDispatcher;
70         mConfigurationController = configurationController;
71         mIconControllerCreators = iconControllerCreators;
72         mIconTag = mResources.getString(R.string.qc_icon_tag);
73         mIconNotHighlightedColor = mContext.getColor(R.color.status_icon_not_highlighted_color);
74         mStatusIconControllerNames = mResources.getStringArray(
75                 getStatusIconControllersStringArray());
76     }
77 
resolve(String className, Map<Class<?>, Provider<T>> creators)78     private static <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
79         try {
80             Class<?> clazz = Class.forName(className);
81             Provider<T> provider = creators.get(clazz);
82             return provider == null ? null : provider.get();
83         } catch (ClassNotFoundException e) {
84             return null;
85         }
86     }
87 
88     /**
89      * Returns the layout res id to use as the button view that contains the StatusIcon.
90      */
91     @LayoutRes
getButtonViewLayout()92     public int getButtonViewLayout() {
93         return R.layout.default_status_icon;
94     }
95 
96     /**
97      * See {@link #addIconViews(ViewGroup, boolean)}
98      */
addIconViews(ViewGroup containerViewGroup)99     public void addIconViews(ViewGroup containerViewGroup) {
100         addIconViews(containerViewGroup, /* shouldAttachPanel= */ true);
101     }
102 
103     /**
104      * Adds Quick Control entry points to the provided container ViewGroup. Also attaches
105      * the Quick Control panel if it's specified and allowed.
106      */
addIconViews(ViewGroup containerViewGroup, boolean shouldAttachPanel)107     public void addIconViews(ViewGroup containerViewGroup, boolean shouldAttachPanel) {
108         LayoutInflater li = LayoutInflater.from(mContext);
109 
110         for (String clsName : mStatusIconControllerNames) {
111             StatusIconController statusIconController = getStatusIconControllerByName(clsName);
112             View entryPointView = li.inflate(getButtonViewLayout(),
113                     containerViewGroup, /* attachToRoot= */ false);
114 
115             ImageView statusIconView = entryPointView.findViewWithTag(mIconTag);
116             statusIconController.registerIconView(statusIconView);
117             statusIconView.setColorFilter(mIconNotHighlightedColor);
118 
119             if (shouldAttachPanel
120                     && statusIconController.getPanelContentLayout() != PANEL_CONTENT_LAYOUT_NONE) {
121                 StatusIconPanelController panelController = new StatusIconPanelController(mContext,
122                         mCarServiceProvider, mBroadcastDispatcher, mConfigurationController);
123                 panelController.attachPanel(entryPointView,
124                         statusIconController.getPanelContentLayout(),
125                         statusIconController.getPanelWidth());
126             }
127             containerViewGroup.addView(entryPointView);
128         }
129     }
130 
131     @ArrayRes
getStatusIconControllersStringArray()132     protected abstract int getStatusIconControllersStringArray();
133 
getStatusIconControllerByName(String className)134     private StatusIconController getStatusIconControllerByName(String className) {
135         try {
136             StatusIconController statusIconController = resolveStatusIconController(className);
137             if (statusIconController == null) {
138                 Constructor constructor = Class.forName(className).getConstructor(Context.class);
139                 statusIconController = (StatusIconController) constructor.newInstance(this);
140             }
141 
142             return statusIconController;
143         } catch (ClassNotFoundException
144                 | NoSuchMethodException
145                 | IllegalAccessException
146                 | InstantiationException
147                 | InvocationTargetException ex) {
148             throw new RuntimeException(ex);
149         }
150     }
151 
resolveStatusIconController(String className)152     private StatusIconController resolveStatusIconController(String className) {
153         return resolve(className, mIconControllerCreators);
154     }
155 }
156