• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.hvac;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorInflater;
21 import android.animation.ValueAnimator;
22 import android.content.Context;
23 import android.content.res.Configuration;
24 import android.content.res.Resources;
25 import android.graphics.Rect;
26 import android.os.Build;
27 import android.os.Handler;
28 import android.util.Log;
29 import android.view.KeyEvent;
30 import android.view.LayoutInflater;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.view.WindowInsets;
35 
36 import androidx.annotation.Nullable;
37 
38 import com.android.systemui.R;
39 import com.android.systemui.car.CarDeviceProvisionedController;
40 import com.android.systemui.car.window.OverlayPanelViewController;
41 import com.android.systemui.car.window.OverlayViewGlobalStateController;
42 import com.android.systemui.dagger.SysUISingleton;
43 import com.android.systemui.dagger.qualifiers.Main;
44 import com.android.systemui.statusbar.policy.ConfigurationController;
45 import com.android.wm.shell.animation.FlingAnimationUtils;
46 
47 import javax.inject.Inject;
48 
49 @SysUISingleton
50 public class HvacPanelOverlayViewController extends OverlayPanelViewController implements
51         ConfigurationController.ConfigurationListener {
52     private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG;
53     private static final String TAG = HvacPanelOverlayViewController.class.getName();
54 
55     private final Context mContext;
56     private final Resources mResources;
57     private final Handler mHandler;
58     private final HvacController mHvacController;
59     private final float mFullyOpenDimAmount;
60     private final int mAutoDismissDurationMs;
61 
62     private boolean mIsUiModeNight;
63     private float mCurrentDimAmount = 0f;
64     @Nullable
65     private Animator mOpenAnimator;
66     @Nullable
67     private Animator mCloseAnimator;
68 
69     private HvacPanelView mHvacPanelView;
70 
71     private final Runnable mAutoDismiss = () -> {
72         if (isPanelExpanded()) {
73             toggle();
74         }
75     };
76 
77     @Inject
HvacPanelOverlayViewController(Context context, @Main Resources resources, @Main Handler handler, HvacController hvacController, OverlayViewGlobalStateController overlayViewGlobalStateController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, CarDeviceProvisionedController carDeviceProvisionedController, ConfigurationController configurationController)78     public HvacPanelOverlayViewController(Context context,
79             @Main Resources resources,
80             @Main Handler handler,
81             HvacController hvacController,
82             OverlayViewGlobalStateController overlayViewGlobalStateController,
83             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
84             CarDeviceProvisionedController carDeviceProvisionedController,
85             ConfigurationController configurationController) {
86         super(context, resources, R.id.hvac_panel_stub, overlayViewGlobalStateController,
87                 flingAnimationUtilsBuilder, carDeviceProvisionedController);
88         mContext = context;
89         mResources = resources;
90         mHandler = handler;
91         mHvacController = hvacController;
92         configurationController.addCallback(this);
93         mFullyOpenDimAmount = mResources.getFloat(R.fraction.hvac_overlay_window_dim_amount);
94         mAutoDismissDurationMs = mResources.getInteger(R.integer.config_hvacAutoDismissDurationMs);
95     }
96 
97     @Override
onFinishInflate()98     protected void onFinishInflate() {
99         super.onFinishInflate();
100 
101         View closeButton = getLayout().findViewById(R.id.hvac_panel_close_button);
102         if (closeButton != null) {
103             closeButton.setOnClickListener(v -> dismissHvacPanel());
104         }
105 
106         mHvacPanelView = getLayout().findViewById(R.id.hvac_panel);
107         mHvacController.registerHvacViews(mHvacPanelView);
108 
109         mHvacPanelView.setKeyEventHandler((event) -> {
110             if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
111                 return false;
112             }
113 
114             if (event.getAction() == KeyEvent.ACTION_UP && isPanelExpanded()) {
115                 dismissHvacPanel();
116             }
117             return true;
118         });
119 
120         mHvacPanelView.setMotionEventHandler((event -> {
121             setAutoDismissTimeout();
122         }));
123 
124         loadCustomAnimators();
125     }
126 
127     @Override
getInsetTypesToFit()128     protected int getInsetTypesToFit() {
129         return WindowInsets.Type.systemBars();
130     }
131 
132     @Override
shouldShowStatusBarInsets()133     protected boolean shouldShowStatusBarInsets() {
134         return true;
135     }
136 
137     @Override
shouldShowNavigationBarInsets()138     protected boolean shouldShowNavigationBarInsets() {
139         return true;
140     }
141 
142     @Override
shouldAnimateCollapsePanel()143     protected boolean shouldAnimateCollapsePanel() {
144         return true;
145     }
146 
147     @Override
shouldAnimateExpandPanel()148     protected boolean shouldAnimateExpandPanel() {
149         return true;
150     }
151 
152     @Override
shouldAllowClosingScroll()153     protected boolean shouldAllowClosingScroll() {
154         return true;
155     }
156 
157     @Override
getDefaultDimAmount()158     protected float getDefaultDimAmount() {
159         return mCurrentDimAmount;
160     }
161 
162     @Override
getHandleBarViewId()163     protected Integer getHandleBarViewId() {
164         return R.id.handle_bar;
165     }
166 
167     @Override
getFocusAreaViewId()168     protected int getFocusAreaViewId() {
169         return R.id.hvac_panel_container;
170     }
171 
172     @Override
getSettleClosePercentage()173     protected int getSettleClosePercentage() {
174         return mResources.getInteger(R.integer.hvac_panel_settle_close_percentage);
175     }
176 
177     @Override
onAnimateExpandPanel()178     protected void onAnimateExpandPanel() {
179         setAutoDismissTimeout();
180     }
181 
182     @Override
onAnimateCollapsePanel()183     protected void onAnimateCollapsePanel() {
184         removeAutoDismissTimeout();
185     }
186 
187     @Override
onCollapseAnimationEnd()188     protected void onCollapseAnimationEnd() {
189         // no-op.
190     }
191 
192     @Override
onExpandAnimationEnd()193     protected void onExpandAnimationEnd() {
194         // no-op.
195     }
196 
197     @Override
onOpenScrollStart()198     protected void onOpenScrollStart() {
199         // no-op.
200     }
201 
202     @Override
onTouchEvent(View view, MotionEvent event)203     protected void onTouchEvent(View view, MotionEvent event) {
204         if (mHvacPanelView == null) {
205             return;
206         }
207         Rect outBounds = new Rect();
208         mHvacPanelView.getBoundsInWindow(outBounds, /* clipToParent= */ true);
209         if (isPanelExpanded() && (event.getAction() == MotionEvent.ACTION_UP)
210                 && isTouchOutside(outBounds, event.getX(), event.getY())) {
211             dismissHvacPanel();
212         }
213     }
214 
215     @Override
onScroll(int y)216     protected void onScroll(int y) {
217         super.onScroll(y);
218 
219         float percentageOpen =
220                 ((float) (mAnimateDirection > 0 ? y : getLayout().getHeight() - y))
221                         / getLayout().getHeight();
222         mCurrentDimAmount = mFullyOpenDimAmount * percentageOpen;
223         getOverlayViewGlobalStateController().updateWindowDimBehind(this, mCurrentDimAmount);
224     }
225 
isTouchOutside(Rect bounds, float x, float y)226     private boolean isTouchOutside(Rect bounds, float x, float y) {
227         return x < bounds.left || x > bounds.right || y < bounds.top || y > bounds.bottom;
228     }
229 
dismissHvacPanel()230     private void dismissHvacPanel() {
231         removeAutoDismissTimeout();
232         mHandler.post(mAutoDismiss);
233     }
234 
setAutoDismissTimeout()235     private void setAutoDismissTimeout() {
236         if (mAutoDismissDurationMs > 0) {
237             mHandler.removeCallbacks(mAutoDismiss);
238             mHandler.postDelayed(mAutoDismiss, mAutoDismissDurationMs);
239         }
240     }
241 
removeAutoDismissTimeout()242     private void removeAutoDismissTimeout() {
243         if (mAutoDismissDurationMs > 0) {
244             mHandler.removeCallbacks(mAutoDismiss);
245         }
246     }
247 
loadCustomAnimators()248     private void loadCustomAnimators() {
249         try {
250             mOpenAnimator = AnimatorInflater.loadAnimator(mContext, R.anim.hvac_open_anim);
251             mOpenAnimator.setTarget(getLayout());
252         } catch (Resources.NotFoundException e) {
253             if (DEBUG) {
254                 Log.d(TAG, "Custom open animator not found - using default");
255             }
256         }
257 
258         try {
259             mCloseAnimator = AnimatorInflater.loadAnimator(mContext, R.anim.hvac_close_anim);
260             mCloseAnimator.setTarget(getLayout());
261         } catch (Resources.NotFoundException e) {
262             if (DEBUG) {
263                 Log.d(TAG, "Custom close animator not found - using default");
264             }
265         }
266     }
267 
268     @Override
getCustomAnimator(float from, float to, float velocity, boolean isClosing)269     protected Animator getCustomAnimator(float from, float to, float velocity, boolean isClosing) {
270         Animator animator = isClosing ? mCloseAnimator : mOpenAnimator;
271         if (animator != null) {
272             animator.removeAllListeners();
273             if (animator instanceof ValueAnimator) {
274                 ((ValueAnimator) animator).setFloatValues(from, to);
275             }
276         }
277 
278         return animator;
279     }
280 
281     @Override
onConfigChanged(Configuration newConfig)282     public void onConfigChanged(Configuration newConfig) {
283         boolean isConfigNightMode = newConfig.isNightModeActive();
284 
285         // Only refresh UI on Night mode changes
286         if (isConfigNightMode != mIsUiModeNight) {
287             mIsUiModeNight = isConfigNightMode;
288 
289             if (getLayout() == null) return;
290             mHvacPanelView = getLayout().findViewById(R.id.hvac_panel);
291             if (mHvacPanelView == null) return;
292             ViewGroup hvacViewGroupParent = (ViewGroup) mHvacPanelView.getParent();
293 
294             // cache properties of {@link HvacPanelView}
295             int inflatedId = mHvacPanelView.getId();
296             ViewGroup.LayoutParams layoutParams = mHvacPanelView.getLayoutParams();
297             HvacPanelView.KeyEventHandler hvacKeyEventHandler = mHvacPanelView
298                     .getKeyEventHandler();
299             int indexOfView = hvacViewGroupParent.indexOfChild(mHvacPanelView);
300 
301             // remove {@link HvacPanelView} from its parent and reinflate it
302             hvacViewGroupParent.removeView(mHvacPanelView);
303             HvacPanelView newHvacPanelView = (HvacPanelView) LayoutInflater.from(mContext).inflate(
304                     R.layout.hvac_panel, /* root= */ hvacViewGroupParent,
305                     /* attachToRoot= */ false);
306             hvacViewGroupParent.addView(newHvacPanelView, indexOfView);
307             mHvacPanelView = newHvacPanelView;
308 
309             // reset {@link HvacPanelView} cached properties
310             mHvacPanelView.setId(inflatedId);
311             mHvacPanelView.setLayoutParams(layoutParams);
312             mHvacController.registerHvacViews(mHvacPanelView);
313             mHvacPanelView.setKeyEventHandler(hvacKeyEventHandler);
314         }
315     }
316 }
317