blob: 6fba1d516c73593838298580d704f720552bbaa5 [file] [log] [blame]
Heemin Seog1a39dea2019-10-16 15:58:21 -07001/*
2 * Copyright (C) 2019 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
17package com.android.systemui.navigationbar.car;
18
Heemin Seog1a39dea2019-10-16 15:58:21 -070019import android.content.Context;
20import android.graphics.PixelFormat;
21import android.inputmethodservice.InputMethodService;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.view.Display;
27import android.view.Gravity;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.WindowManager;
31
32import com.android.internal.statusbar.IStatusBarService;
33import com.android.internal.statusbar.RegisterStatusBarResult;
34import com.android.systemui.R;
35import com.android.systemui.SystemUI;
Dave Mankofff4736812019-10-18 17:25:50 -040036import com.android.systemui.dagger.qualifiers.MainHandler;
Heemin Seog1a39dea2019-10-16 15:58:21 -070037import com.android.systemui.shared.system.ActivityManagerWrapper;
38import com.android.systemui.statusbar.CommandQueue;
39import com.android.systemui.statusbar.NavigationBarController;
40import com.android.systemui.statusbar.car.hvac.HvacController;
41import com.android.systemui.statusbar.car.hvac.TemperatureView;
42import com.android.systemui.statusbar.policy.DeviceProvisionedController;
43import com.android.systemui.statusbar.policy.KeyguardStateController;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47
48import javax.inject.Inject;
Heemin Seog1a39dea2019-10-16 15:58:21 -070049
50import dagger.Lazy;
51
52/** Navigation bars customized for the automotive use case. */
53public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks {
54
55 private final NavigationBarViewFactory mNavigationBarViewFactory;
56 private final WindowManager mWindowManager;
57 private final DeviceProvisionedController mDeviceProvisionedController;
58 private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListener;
59 private final Handler mMainHandler;
60 private final Lazy<KeyguardStateController> mKeyguardStateController;
61 private final Lazy<CarFacetButtonController> mFacetButtonController;
62 private final Lazy<NavigationBarController> mNavigationBarController;
63 private final Lazy<HvacController> mHvacController;
64
65 private IStatusBarService mBarService;
66 private CommandQueue mCommandQueue;
67 private ActivityManagerWrapper mActivityManagerWrapper;
68
69 // If the nav bar should be hidden when the soft keyboard is visible.
70 private boolean mHideNavBarForKeyboard;
71 private boolean mBottomNavBarVisible;
72
73 // Nav bar views.
74 private ViewGroup mNavigationBarWindow;
75 private ViewGroup mLeftNavigationBarWindow;
76 private ViewGroup mRightNavigationBarWindow;
77 private CarNavigationBarView mNavigationBarView;
78 private CarNavigationBarView mLeftNavigationBarView;
79 private CarNavigationBarView mRightNavigationBarView;
80
81 // To be attached to the navigation bars such that they can close the notification panel if
82 // it's open.
83 private boolean mDeviceIsSetUpForUser = true;
84
85 // Configuration values for if nav bars should be shown.
86 private boolean mShowBottom;
87 private boolean mShowLeft;
88 private boolean mShowRight;
89
90
91 @Inject
92 public CarNavigationBar(Context context,
93 NavigationBarViewFactory navigationBarViewFactory,
94 WindowManager windowManager,
95 DeviceProvisionedController deviceProvisionedController,
96 Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListener,
Dave Mankofff4736812019-10-18 17:25:50 -040097 @MainHandler Handler mainHandler,
Heemin Seog1a39dea2019-10-16 15:58:21 -070098 Lazy<KeyguardStateController> keyguardStateController,
99 Lazy<CarFacetButtonController> facetButtonController,
100 Lazy<NavigationBarController> navigationBarController,
101 Lazy<HvacController> hvacController) {
102 super(context);
103 mNavigationBarViewFactory = navigationBarViewFactory;
104 mWindowManager = windowManager;
105 mDeviceProvisionedController = deviceProvisionedController;
106 mFacetButtonTaskStackListener = facetButtonTaskStackListener;
107 mMainHandler = mainHandler;
108 mKeyguardStateController = keyguardStateController;
109 mFacetButtonController = facetButtonController;
110 mNavigationBarController = navigationBarController;
111 mHvacController = hvacController;
112 }
113
114 @Override
115 public void start() {
116 // Set initial state.
117 mHideNavBarForKeyboard = mContext.getResources().getBoolean(
118 com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
119 mBottomNavBarVisible = false;
120
121 // Read configuration.
122 mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
123 mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
124 mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
125
126 // Get bar service.
127 mBarService = IStatusBarService.Stub.asInterface(
128 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
129
130 // Connect into the status bar manager service
131 mCommandQueue = getComponent(CommandQueue.class);
132 mCommandQueue.addCallback(this);
133
134 RegisterStatusBarResult result = null;
135 try {
136 result = mBarService.registerStatusBar(mCommandQueue);
137 } catch (RemoteException ex) {
138 ex.rethrowFromSystemServer();
139 }
140
141 mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
142 mDeviceProvisionedController.addCallback(
143 new DeviceProvisionedController.DeviceProvisionedListener() {
144 @Override
145 public void onUserSetupChanged() {
146 mMainHandler.post(() -> restartNavBarsIfNecessary());
147 }
148
149 @Override
150 public void onUserSwitched() {
151 mMainHandler.post(() -> restartNavBarsIfNecessary());
152 }
153 });
154
155 createNavigationBar(result);
156
157 mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
158 mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListener.get());
159
160 mHvacController.get().connectToCarService();
161 }
162
163 private void restartNavBarsIfNecessary() {
164 boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
165 if (mDeviceIsSetUpForUser != currentUserSetup) {
166 mDeviceIsSetUpForUser = currentUserSetup;
167 restartNavBars();
168 }
169 }
170
171 /**
172 * Remove all content from navbars and rebuild them. Used to allow for different nav bars
173 * before and after the device is provisioned. . Also for change of density and font size.
174 */
175 private void restartNavBars() {
176 // remove and reattach all hvac components such that we don't keep a reference to unused
177 // ui elements
178 mHvacController.get().removeAllComponents();
179 mFacetButtonController.get().removeAll();
180
181 if (mNavigationBarWindow != null) {
182 mNavigationBarWindow.removeAllViews();
183 mNavigationBarView = null;
184 }
185
186 if (mLeftNavigationBarWindow != null) {
187 mLeftNavigationBarWindow.removeAllViews();
188 mLeftNavigationBarView = null;
189 }
190
191 if (mRightNavigationBarWindow != null) {
192 mRightNavigationBarWindow.removeAllViews();
193 mRightNavigationBarView = null;
194 }
195
196 buildNavBarContent();
197 // If the UI was rebuilt (day/night change) while the keyguard was up we need to
198 // correctly respect that state.
199 if (mKeyguardStateController.get().isShowing()) {
200 updateNavBarForKeyguardContent();
201 }
202
203 // CarFacetButtonController was reset therefore we need to re-add the status bar elements
204 // to the controller.
205 // TODO(hseog): Add facet buttons in status bar to controller.
206 }
207
208 private void createNavigationBar(RegisterStatusBarResult result) {
209 buildNavBarWindows();
210 buildNavBarContent();
211 attachNavBarWindows();
212
213 // Try setting up the initial state of the nav bar if applicable.
214 if (result != null) {
215 setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
216 result.mImeWindowVis, result.mImeBackDisposition,
217 result.mShowImeSwitcher);
218 }
219
220 // There has been a car customized nav bar on the default display, so just create nav bars
221 // on external displays.
222 mNavigationBarController.get().createNavigationBars(/* includeDefaultDisplay= */ false,
223 result);
224 }
225
226 private void buildNavBarWindows() {
227 if (mShowBottom) {
228 mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
229 }
230
231 if (mShowLeft) {
232 mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
233 }
234
235 if (mShowRight) {
236 mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
237 }
238 }
239
240 private void buildNavBarContent() {
241 if (mShowBottom) {
242 mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
243 mNavigationBarWindow.addView(mNavigationBarView);
244 addTemperatureViewToController(mNavigationBarView);
245 }
246
247 if (mShowLeft) {
248 mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
249 mLeftNavigationBarWindow.addView(mLeftNavigationBarView);
250 addTemperatureViewToController(mLeftNavigationBarView);
251 }
252
253 if (mShowRight) {
254 mRightNavigationBarView = mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser);
255 mRightNavigationBarWindow.addView(mRightNavigationBarView);
256 // Add ability to toggle notification center.
257 addTemperatureViewToController(mRightNavigationBarView);
258 // Add ability to close notification center on touch.
259 }
260 }
261
262 private void addTemperatureViewToController(View v) {
263 if (v instanceof TemperatureView) {
264 mHvacController.get().addHvacTextView((TemperatureView) v);
265 } else if (v instanceof ViewGroup) {
266 ViewGroup viewGroup = (ViewGroup) v;
267 for (int i = 0; i < viewGroup.getChildCount(); i++) {
268 addTemperatureViewToController(viewGroup.getChildAt(i));
269 }
270 }
271 }
272
273 private void attachNavBarWindows() {
274 if (mShowBottom && !mBottomNavBarVisible) {
275 mBottomNavBarVisible = true;
276
277 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
278 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
279 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
280 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
281 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
282 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
283 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
284 PixelFormat.TRANSLUCENT);
285 lp.setTitle("CarNavigationBar");
286 lp.windowAnimations = 0;
287 mWindowManager.addView(mNavigationBarWindow, lp);
288 }
289
290 if (mShowLeft) {
291 int width = mContext.getResources().getDimensionPixelSize(
292 R.dimen.car_left_navigation_bar_width);
293 WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
294 width, ViewGroup.LayoutParams.MATCH_PARENT,
295 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
296 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
297 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
298 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
299 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
300 PixelFormat.TRANSLUCENT);
301 leftlp.setTitle("LeftCarNavigationBar");
302 leftlp.windowAnimations = 0;
303 leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
304 leftlp.gravity = Gravity.LEFT;
305 mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
306 }
307 if (mShowRight) {
308 int width = mContext.getResources().getDimensionPixelSize(
309 R.dimen.car_right_navigation_bar_width);
310 WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
311 width, ViewGroup.LayoutParams.MATCH_PARENT,
312 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
313 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
314 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
315 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
316 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
317 PixelFormat.TRANSLUCENT);
318 rightlp.setTitle("RightCarNavigationBar");
319 rightlp.windowAnimations = 0;
320 rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
321 rightlp.gravity = Gravity.RIGHT;
322 mWindowManager.addView(mRightNavigationBarWindow, rightlp);
323 }
324 }
325
326 /**
327 * We register for soft keyboard visibility events such that we can hide the navigation bar
328 * giving more screen space to the IME. Note: this is optional and controlled by
329 * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
330 */
331 @Override
332 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
333 boolean showImeSwitcher) {
334 if (!mHideNavBarForKeyboard) {
335 return;
336 }
337
338 if (mContext.getDisplay().getDisplayId() != displayId) {
339 return;
340 }
341
342 boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
343 showBottomNavBarWindow(isKeyboardVisible);
344 }
345
346 private void showBottomNavBarWindow(boolean isKeyboardVisible) {
347 if (!mShowBottom) {
348 return;
349 }
350
351 // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
352 // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
353 if (isKeyboardVisible ^ mBottomNavBarVisible) {
354 return;
355 }
356
357 mNavigationBarViewFactory.getBottomWindow().setVisibility(
358 isKeyboardVisible ? View.GONE : View.VISIBLE);
359 mBottomNavBarVisible = !isKeyboardVisible;
360 }
361
362 private void updateNavBarForKeyguardContent() {
363 if (mNavigationBarView != null) {
364 mNavigationBarView.showKeyguardButtons();
365 }
366 if (mLeftNavigationBarView != null) {
367 mLeftNavigationBarView.showKeyguardButtons();
368 }
369 if (mRightNavigationBarView != null) {
370 mRightNavigationBarView.showKeyguardButtons();
371 }
372 }
373
374 @Override
375 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
376 pw.print(" mTaskStackListener=");
377 pw.println(mFacetButtonTaskStackListener.get());
378 pw.print(" mNavigationBarView=");
379 pw.println(mNavigationBarView);
380 }
381}