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.app.UiModeManager;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.graphics.Rect;
24 import android.view.KeyEvent;
25 import android.view.LayoutInflater;
26 import android.view.MotionEvent;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.WindowInsets;
30 
31 import com.android.systemui.R;
32 import com.android.systemui.car.CarDeviceProvisionedController;
33 import com.android.systemui.car.window.OverlayPanelViewController;
34 import com.android.systemui.car.window.OverlayViewGlobalStateController;
35 import com.android.systemui.dagger.SysUISingleton;
36 import com.android.systemui.dagger.qualifiers.Main;
37 import com.android.systemui.statusbar.policy.ConfigurationController;
38 import com.android.wm.shell.animation.FlingAnimationUtils;
39 
40 import javax.inject.Inject;
41 
42 @SysUISingleton
43 public class HvacPanelOverlayViewController extends OverlayPanelViewController implements
44         ConfigurationController.ConfigurationListener {
45 
46     private final Context mContext;
47     private final Resources mResources;
48     private final HvacController mHvacController;
49     private final UiModeManager mUiModeManager;
50     private final float mFullyOpenDimAmount;
51 
52     private boolean mIsUiModeNight;
53     private float mCurrentDimAmount = 0f;
54 
55     private HvacPanelView mHvacPanelView;
56 
57     @Inject
HvacPanelOverlayViewController(Context context, @Main Resources resources, HvacController hvacController, OverlayViewGlobalStateController overlayViewGlobalStateController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, CarDeviceProvisionedController carDeviceProvisionedController, ConfigurationController configurationController, UiModeManager uiModeManager)58     public HvacPanelOverlayViewController(Context context,
59             @Main Resources resources,
60             HvacController hvacController,
61             OverlayViewGlobalStateController overlayViewGlobalStateController,
62             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
63             CarDeviceProvisionedController carDeviceProvisionedController,
64             ConfigurationController configurationController,
65             UiModeManager uiModeManager) {
66         super(context, resources, R.id.hvac_panel_stub, overlayViewGlobalStateController,
67                 flingAnimationUtilsBuilder, carDeviceProvisionedController);
68         mContext = context;
69         mResources = resources;
70         mHvacController = hvacController;
71         mUiModeManager = uiModeManager;
72         configurationController.addCallback(this);
73         mFullyOpenDimAmount = mContext.getResources().getFloat(
74                 R.fraction.hvac_overlay_window_dim_amount);
75     }
76 
77     @Override
onFinishInflate()78     protected void onFinishInflate() {
79         super.onFinishInflate();
80 
81         View closeButton = getLayout().findViewById(R.id.hvac_panel_close_button);
82         if (closeButton != null) {
83             closeButton.setOnClickListener(v -> toggle());
84         }
85 
86         mHvacPanelView = getLayout().findViewById(R.id.hvac_panel);
87         mHvacController.registerHvacViews(mHvacPanelView);
88 
89         mHvacPanelView.setKeyEventHandler((event) -> {
90             if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
91                 return false;
92             }
93 
94             if (event.getAction() == KeyEvent.ACTION_UP && isPanelExpanded()) {
95                 toggle();
96             }
97             return true;
98         });
99     }
100 
101     @Override
getInsetTypesToFit()102     protected int getInsetTypesToFit() {
103         return WindowInsets.Type.systemBars();
104     }
105 
106     @Override
shouldShowStatusBarInsets()107     protected boolean shouldShowStatusBarInsets() {
108         return true;
109     }
110 
111     @Override
shouldShowNavigationBarInsets()112     protected boolean shouldShowNavigationBarInsets() {
113         return true;
114     }
115 
116     @Override
shouldAnimateCollapsePanel()117     protected boolean shouldAnimateCollapsePanel() {
118         return true;
119     }
120 
121     @Override
shouldAnimateExpandPanel()122     protected boolean shouldAnimateExpandPanel() {
123         return true;
124     }
125 
126     @Override
shouldAllowClosingScroll()127     protected boolean shouldAllowClosingScroll() {
128         return true;
129     }
130 
131     @Override
getDefaultDimAmount()132     protected float getDefaultDimAmount() {
133         return mCurrentDimAmount;
134     }
135 
136     @Override
getHandleBarViewId()137     protected Integer getHandleBarViewId() {
138         return R.id.handle_bar;
139     }
140 
141     @Override
getFocusAreaViewId()142     protected int getFocusAreaViewId() {
143         return R.id.hvac_panel_container;
144     }
145 
146     @Override
getSettleClosePercentage()147     protected int getSettleClosePercentage() {
148         return mResources.getInteger(R.integer.hvac_panel_settle_close_percentage);
149     }
150 
151     @Override
onAnimateCollapsePanel()152     protected void onAnimateCollapsePanel() {
153         // no-op.
154     }
155 
156     @Override
onAnimateExpandPanel()157     protected void onAnimateExpandPanel() {
158         // no-op.
159     }
160 
161     @Override
onCollapseAnimationEnd()162     protected void onCollapseAnimationEnd() {
163         // no-op.
164     }
165 
166     @Override
onExpandAnimationEnd()167     protected void onExpandAnimationEnd() {
168         // no-op.
169     }
170 
171     @Override
onOpenScrollStart()172     protected void onOpenScrollStart() {
173         // no-op.
174     }
175 
176     @Override
onTouchEvent(View view, MotionEvent event)177     protected void onTouchEvent(View view, MotionEvent event) {
178         if (mHvacPanelView == null) {
179             return;
180         }
181         Rect outBounds = new Rect();
182         mHvacPanelView.getBoundsInWindow(outBounds, /* clipToParent= */ true);
183         if (isPanelExpanded() && (event.getAction() == MotionEvent.ACTION_UP)
184                 && isTouchOutside(outBounds, event.getX(), event.getY())) {
185             toggle();
186         }
187     }
188 
189     @Override
onScroll(int y)190     protected void onScroll(int y) {
191         super.onScroll(y);
192 
193         float percentageOpen =
194                 ((float) (mAnimateDirection > 0 ? y : getLayout().getHeight() - y))
195                         / getLayout().getHeight();
196         mCurrentDimAmount = mFullyOpenDimAmount * percentageOpen;
197         getOverlayViewGlobalStateController().updateWindowDimBehind(this, mCurrentDimAmount);
198     }
199 
isTouchOutside(Rect bounds, float x, float y)200     private boolean isTouchOutside(Rect bounds, float x, float y) {
201         return x < bounds.left || x > bounds.right || y < bounds.top || y > bounds.bottom;
202     }
203 
204     @Override
onConfigChanged(Configuration newConfig)205     public void onConfigChanged(Configuration newConfig) {
206         boolean isConfigNightMode = newConfig.isNightModeActive();
207 
208         // Only refresh UI on Night mode changes
209         if (isConfigNightMode != mIsUiModeNight) {
210             mIsUiModeNight = isConfigNightMode;
211             mUiModeManager.setNightModeActivated(mIsUiModeNight);
212 
213             if (getLayout() == null) return;
214             mHvacPanelView = getLayout().findViewById(R.id.hvac_panel);
215             if (mHvacPanelView == null) return;
216             ViewGroup hvacViewGroupParent = (ViewGroup) mHvacPanelView.getParent();
217 
218             // cache properties of {@link HvacPanelView}
219             int inflatedId = mHvacPanelView.getId();
220             ViewGroup.LayoutParams layoutParams = mHvacPanelView.getLayoutParams();
221             HvacPanelView.KeyEventHandler hvacKeyEventHandler = mHvacPanelView
222                     .getKeyEventHandler();
223             int indexOfView = hvacViewGroupParent.indexOfChild(mHvacPanelView);
224 
225             // remove {@link HvacPanelView} from its parent and reinflate it
226             hvacViewGroupParent.removeView(mHvacPanelView);
227             HvacPanelView newHvacPanelView = (HvacPanelView) LayoutInflater.from(mContext).inflate(
228                     R.layout.hvac_panel, /* root= */ hvacViewGroupParent,
229                     /* attachToRoot= */ false);
230             hvacViewGroupParent.addView(newHvacPanelView, indexOfView);
231             mHvacPanelView = newHvacPanelView;
232 
233             // reset {@link HvacPanelView} cached properties
234             mHvacPanelView.setId(inflatedId);
235             mHvacPanelView.setLayoutParams(layoutParams);
236             mHvacController.registerHvacViews(mHvacPanelView);
237             mHvacPanelView.setKeyEventHandler(hvacKeyEventHandler);
238         }
239     }
240 }
241