• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.complication;
18 
19 import static com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
20 import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
21 import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS;
22 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
23 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
24 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
25 
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.res.Resources;
29 import android.graphics.drawable.Drawable;
30 import android.util.Log;
31 import android.view.View;
32 import android.widget.ImageView;
33 
34 import androidx.annotation.Nullable;
35 
36 import com.android.internal.logging.UiEventLogger;
37 import com.android.settingslib.Utils;
38 import com.android.systemui.CoreStartable;
39 import com.android.systemui.animation.ActivityTransitionAnimator;
40 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
41 import com.android.systemui.controls.ControlsServiceInfo;
42 import com.android.systemui.controls.dagger.ControlsComponent;
43 import com.android.systemui.controls.management.ControlsListingController;
44 import com.android.systemui.controls.ui.ControlsActivity;
45 import com.android.systemui.controls.ui.ControlsUiController;
46 import com.android.systemui.dagger.qualifiers.Main;
47 import com.android.systemui.dagger.qualifiers.SystemUser;
48 import com.android.systemui.dreams.DreamOverlayStateController;
49 import com.android.systemui.plugins.ActivityStarter;
50 import com.android.systemui.shared.condition.Monitor;
51 import com.android.systemui.statusbar.policy.ConfigurationController;
52 import com.android.systemui.util.ViewController;
53 import com.android.systemui.util.condition.ConditionalCoreStartable;
54 
55 import java.util.List;
56 
57 import javax.inject.Inject;
58 import javax.inject.Named;
59 
60 /**
61  * A dream complication that shows a home controls chip to launch device controls (to control
62  * devices at home like lights and thermostats).
63  */
64 public class DreamHomeControlsComplication implements Complication {
65     private final Resources mResources;
66     private final DreamHomeControlsComplicationComponent.Factory mComponentFactory;
67 
68     @Inject
DreamHomeControlsComplication( @ain Resources resources, DreamHomeControlsComplicationComponent.Factory componentFactory)69     public DreamHomeControlsComplication(
70             @Main Resources resources,
71             DreamHomeControlsComplicationComponent.Factory componentFactory) {
72         mResources = resources;
73         mComponentFactory = componentFactory;
74     }
75 
76     @Override
createView(ComplicationViewModel model)77     public ViewHolder createView(ComplicationViewModel model) {
78         return mComponentFactory.create(mResources).getViewHolder();
79     }
80 
81     @Override
getRequiredTypeAvailability()82     public int getRequiredTypeAvailability() {
83         return COMPLICATION_TYPE_HOME_CONTROLS;
84     }
85 
86     /**
87      * {@link CoreStartable} for registering the complication with SystemUI on startup.
88      */
89     public static class Registrant extends ConditionalCoreStartable {
90         private final DreamHomeControlsComplication mComplication;
91         private final DreamOverlayStateController mDreamOverlayStateController;
92         private final ControlsComponent mControlsComponent;
93         private final boolean mReplacedByOpenHub;
94 
95         private boolean mOverlayActive = false;
96 
97         // Callback for when the home controls service availability changes.
98         private final ControlsListingController.ControlsListingCallback mControlsCallback =
99                 services -> updateHomeControlsComplication();
100 
101         private final DreamOverlayStateController.Callback mOverlayStateCallback =
102                 new DreamOverlayStateController.Callback() {
103                     @Override
104                     public void onStateChanged() {
105                         if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) {
106                             return;
107                         }
108 
109                         mOverlayActive = !mOverlayActive;
110 
111                         if (mOverlayActive) {
112                             updateHomeControlsComplication();
113                         }
114                     }
115                 };
116 
117         @Inject
Registrant(DreamHomeControlsComplication complication, DreamOverlayStateController dreamOverlayStateController, ControlsComponent controlsComponent, @SystemUser Monitor monitor, @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replacedByOpenHub)118         public Registrant(DreamHomeControlsComplication complication,
119                 DreamOverlayStateController dreamOverlayStateController,
120                 ControlsComponent controlsComponent,
121                 @SystemUser Monitor monitor,
122                 @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replacedByOpenHub) {
123             super(monitor);
124             mComplication = complication;
125             mControlsComponent = controlsComponent;
126             mDreamOverlayStateController = dreamOverlayStateController;
127             mReplacedByOpenHub = replacedByOpenHub;
128         }
129 
130         @Override
onStart()131         public void onStart() {
132             mControlsComponent.getControlsListingController().ifPresent(
133                     c -> c.addCallback(mControlsCallback));
134             mDreamOverlayStateController.addCallback(mOverlayStateCallback);
135         }
136 
updateHomeControlsComplication()137         private void updateHomeControlsComplication() {
138             mControlsComponent.getControlsListingController().ifPresent(c -> {
139                 if (isHomeControlsAvailable(c.getCurrentServices())) {
140                     mDreamOverlayStateController.addComplication(mComplication);
141                 } else {
142                     mDreamOverlayStateController.removeComplication(mComplication);
143                 }
144             });
145         }
146 
isHomeControlsAvailable(List<ControlsServiceInfo> controlsServices)147         private boolean isHomeControlsAvailable(List<ControlsServiceInfo> controlsServices) {
148             if (controlsServices.isEmpty()) {
149                 return false;
150             }
151 
152             final boolean hasFavorites = mControlsComponent.getControlsController()
153                     .map(c -> !c.getFavorites().isEmpty())
154                     .orElse(false);
155             boolean hasPanels = false;
156             for (int i = 0; i < controlsServices.size(); i++) {
157                 if (controlsServices.get(i).getPanelActivity() != null) {
158                     hasPanels = true;
159                     break;
160                 }
161             }
162             final ControlsComponent.Visibility visibility = mControlsComponent.getVisibility();
163             return (hasFavorites || hasPanels) && visibility != UNAVAILABLE;
164         }
165     }
166 
167     /**
168      * Contains values/logic associated with the dream complication view.
169      */
170     public static class DreamHomeControlsChipViewHolder implements ViewHolder {
171         private final ImageView mView;
172         private final ComplicationLayoutParams mLayoutParams;
173         private final DreamHomeControlsChipViewController mViewController;
174 
175         @Inject
DreamHomeControlsChipViewHolder( DreamHomeControlsChipViewController dreamHomeControlsChipViewController, @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view, @Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams )176         DreamHomeControlsChipViewHolder(
177                 DreamHomeControlsChipViewController dreamHomeControlsChipViewController,
178                 @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
179                 @Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
180         ) {
181             mView = view;
182             mLayoutParams = layoutParams;
183             mViewController = dreamHomeControlsChipViewController;
184             mViewController.init();
185         }
186 
187         @Override
getView()188         public ImageView getView() {
189             return mView;
190         }
191 
192         @Override
getLayoutParams()193         public ComplicationLayoutParams getLayoutParams() {
194             return mLayoutParams;
195         }
196     }
197 
198     /**
199      * Controls behavior of the dream complication.
200      */
201     static class DreamHomeControlsChipViewController extends ViewController<ImageView> {
202         private static final String TAG = "DreamHomeControlsCtrl";
203         private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
204 
205         private final ActivityStarter mActivityStarter;
206         private final Context mContext;
207         private final ConfigurationController mConfigurationController;
208         private final ControlsComponent mControlsComponent;
209 
210         private final UiEventLogger mUiEventLogger;
211 
212         private final ConfigurationController.ConfigurationListener mConfigurationListener =
213                 new ConfigurationController.ConfigurationListener() {
214                     @Override
215                     public void onUiModeChanged() {
216                         reloadResources();
217                     }
218                 };
219 
220         @Inject
DreamHomeControlsChipViewController( @amedDREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view, ActivityStarter activityStarter, Context context, ConfigurationController configurationController, ControlsComponent controlsComponent, UiEventLogger uiEventLogger)221         DreamHomeControlsChipViewController(
222                 @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
223                 ActivityStarter activityStarter,
224                 Context context,
225                 ConfigurationController configurationController,
226                 ControlsComponent controlsComponent,
227                 UiEventLogger uiEventLogger) {
228             super(view);
229 
230             mActivityStarter = activityStarter;
231             mContext = context;
232             mConfigurationController = configurationController;
233             mControlsComponent = controlsComponent;
234             mUiEventLogger = uiEventLogger;
235         }
236 
237         @Override
onViewAttached()238         protected void onViewAttached() {
239             reloadResources();
240             mView.setOnClickListener(this::onClickHomeControls);
241             mConfigurationController.addCallback(mConfigurationListener);
242         }
243 
244         @Override
onViewDetached()245         protected void onViewDetached() {
246             mConfigurationController.removeCallback(mConfigurationListener);
247         }
248 
reloadResources()249         private void reloadResources() {
250             final String title = getControlsTitle();
251             if (title != null) {
252                 mView.setContentDescription(title);
253             }
254             mView.setImageResource(mControlsComponent.getTileImageId());
255             mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
256             final Drawable background = mView.getBackground();
257             if (background != null) {
258                 background.setTintList(
259                         Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
260             }
261         }
262 
263         @Nullable
getControlsTitle()264         private String getControlsTitle() {
265             try {
266                 return mContext.getString(mControlsComponent.getTileTitleId());
267             } catch (Resources.NotFoundException e) {
268                 return null;
269             }
270         }
271 
onClickHomeControls(View v)272         private void onClickHomeControls(View v) {
273             if (DEBUG) Log.d(TAG, "home controls complication tapped");
274 
275             mUiEventLogger.log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED);
276 
277             final Intent intent = new Intent(mContext, ControlsActivity.class)
278                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
279                     .putExtra(ControlsUiController.EXTRA_ANIMATE, true)
280                     .putExtra(ControlsUiController.EXIT_TO_DREAM, true);
281 
282             final ActivityTransitionAnimator.Controller controller =
283                     v != null
284                             ? ActivityTransitionAnimator.Controller.fromView(v, null /* cujType */)
285                             : null;
286             if (mControlsComponent.getVisibility() == AVAILABLE) {
287                 // Controls can be made visible.
288                 mActivityStarter.startActivity(intent, true /* dismissShade */, controller,
289                         true /* showOverLockscreenWhenLocked */);
290             } else if (mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK) {
291                 // Controls can be made visible only after device unlock.
292                 mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */,
293                         controller);
294             }
295         }
296     }
297 }
298