• 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.car.displayarea;
18 
19 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
20 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
21 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
22 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
23 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
24 
25 import static com.android.systemui.car.displayarea.CarDisplayAreaOrganizer.BACKGROUND_TASK_CONTAINER;
26 import static com.android.systemui.car.displayarea.CarDisplayAreaOrganizer.CONTROL_BAR_DISPLAY_AREA;
27 import static com.android.systemui.car.displayarea.CarDisplayAreaOrganizer.FEATURE_TITLE_BAR;
28 import static com.android.systemui.car.displayarea.CarDisplayAreaOrganizer.FEATURE_VOICE_PLATE;
29 import static com.android.systemui.car.displayarea.CarDisplayAreaOrganizer.FOREGROUND_DISPLAY_AREA_ROOT;
30 import static com.android.systemui.car.displayarea.DisplayAreaComponent.DISPLAY_AREA_VISIBILITY_CHANGED;
31 import static com.android.systemui.car.displayarea.DisplayAreaComponent.FOREGROUND_DA_STATE.CONTROL_BAR;
32 import static com.android.systemui.car.displayarea.DisplayAreaComponent.FOREGROUND_DA_STATE.DEFAULT;
33 import static com.android.systemui.car.displayarea.DisplayAreaComponent.FOREGROUND_DA_STATE.FULL;
34 import static com.android.systemui.car.displayarea.DisplayAreaComponent.FOREGROUND_DA_STATE.FULL_TO_DEFAULT;
35 import static com.android.systemui.car.displayarea.DisplayAreaComponent.INTENT_EXTRA_IS_DISPLAY_AREA_VISIBLE;
36 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
37 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
38 
39 import android.app.ActivityManager;
40 import android.app.ActivityTaskManager;
41 import android.app.TaskStackListener;
42 import android.app.UiModeManager;
43 import android.car.Car;
44 import android.car.app.CarActivityManager;
45 import android.content.ComponentName;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.pm.PackageManager;
49 import android.content.pm.ResolveInfo;
50 import android.content.res.Configuration;
51 import android.content.res.Resources;
52 import android.graphics.PixelFormat;
53 import android.graphics.Rect;
54 import android.graphics.drawable.Drawable;
55 import android.os.Binder;
56 import android.os.Build;
57 import android.os.Bundle;
58 import android.os.IBinder;
59 import android.os.RemoteException;
60 import android.os.UserHandle;
61 import android.util.ArraySet;
62 import android.util.DisplayMetrics;
63 import android.util.Log;
64 import android.view.Display;
65 import android.view.Gravity;
66 import android.view.IWindowManager;
67 import android.view.LayoutInflater;
68 import android.view.SurfaceControl;
69 import android.view.View;
70 import android.view.ViewGroup;
71 import android.view.WindowInsets;
72 import android.view.WindowManager;
73 import android.view.WindowManagerGlobal;
74 import android.widget.ImageView;
75 import android.window.DisplayAreaAppearedInfo;
76 import android.window.DisplayAreaInfo;
77 import android.window.WindowContainerToken;
78 import android.window.WindowContainerTransaction;
79 
80 import androidx.annotation.Nullable;
81 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
82 
83 import com.android.internal.app.AssistUtils;
84 import com.android.systemui.R;
85 import com.android.systemui.car.CarDeviceProvisionedController;
86 import com.android.systemui.car.CarDeviceProvisionedListener;
87 import com.android.systemui.car.CarServiceProvider;
88 import com.android.systemui.qs.QSHost;
89 import com.android.systemui.statusbar.CommandQueue;
90 import com.android.systemui.statusbar.policy.ConfigurationController;
91 import com.android.systemui.wm.CarUiPortraitDisplaySystemBarsController;
92 import com.android.wm.shell.ShellTaskOrganizer;
93 import com.android.wm.shell.common.HandlerExecutor;
94 import com.android.wm.shell.common.ShellExecutor;
95 import com.android.wm.shell.common.SyncTransactionQueue;
96 
97 import java.util.ArrayList;
98 import java.util.HashMap;
99 import java.util.HashSet;
100 import java.util.List;
101 import java.util.Set;
102 
103 import javax.inject.Inject;
104 
105 /**
106  * Controls the bounds of the home background, audio bar and application displays. This is a
107  * singleton class as there should be one controller used to register and control the DA's
108  */
109 public class CarDisplayAreaController implements ConfigurationController.ConfigurationListener,
110         CommandQueue.Callbacks {
111 
112     // Layer index of how display areas should be placed. Keeping a gap of 100 if we want to
113     // add some other display area layers in between in the future.
114     static final int FOREGROUND_LAYER_INDEX = 0;
115     static final int TITLE_BAR_LAYER_INDEX = 10;
116     static final int BACKGROUND_LAYER_INDEX = 100;
117     static final int CONTROL_BAR_LAYER_INDEX = 200;
118     static final int VOICE_PLATE_LAYER_SHOWN_INDEX = 300;
119     private static final String TAG = "CarDisplayAreaController";
120     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
121     private static final int TITLE_BAR_WINDOW_TYPE =
122             WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
123 
124     private final Rect mControlBarDisplayBounds = new Rect();
125     private final Rect mForegroundApplicationDisplayBounds = new Rect();
126     private final Rect mTitleBarDisplayBounds = new Rect();
127     private final Rect mVoicePlateDisplayBounds = new Rect();
128     private final Rect mBackgroundApplicationDisplayBounds = new Rect();
129     private final Rect mNavBarBounds = new Rect();
130     private final IBinder mWindowToken = new Binder();
131 
132     private final SyncTransactionQueue mSyncQueue;
133     private final CarDisplayAreaOrganizer mOrganizer;
134     private final CarFullscreenTaskListener mCarFullscreenTaskListener;
135     private final ComponentName mControlBarActivityComponent;
136     private final ComponentName mHomeActivityComponent;
137     private final CarUiPortraitDisplaySystemBarsController mCarUiDisplaySystemBarsController;
138     private final CarDeviceProvisionedController mCarDeviceProvisionedController;
139     private final List<ComponentName> mBackgroundActivityComponent;
140     private final HashMap<String, Boolean> mForegroundDAComponentsVisibilityMap;
141     private final ArraySet<ComponentName> mIgnoreOpeningForegroundDAComponentsSet;
142     private final int mTitleBarDragThreshold;
143     private final ShellExecutor mShellExecutor;
144     private final int mEnterExitAnimationDurationMs;
145     // height of DA hosting the control bar.
146     private final int mControlBarDisplayHeight;
147     private final int mDpiDensity;
148     private final int mTotalScreenWidth;
149     // height of DA hosting default apps and covering the maps fully.
150     private final int mFullDisplayHeight;
151     // height of DA hosting default apps and covering the maps to default height.
152     private final int mDefaultDisplayHeight;
153     private final int mTitleBarHeight;
154     private final int mScreenHeightWithoutNavBar;
155     private final int mTotalScreenHeight;
156     private final ComponentName mNotificationCenterComponent;
157     private final CarDisplayAreaTouchHandler mCarDisplayAreaTouchHandler;
158     private final Context mApplicationContext;
159     private final int mForegroundDisplayTop;
160     private final AssistUtils mAssistUtils;
161     private HashSet<Integer> mActiveTasksOnForegroundDA;
162     private HashSet<Integer> mActiveTasksOnBackgroundDA;
163     private final ConfigurationController mConfigurationController;
164     private final UiModeManager mUiModeManager;
165     private DisplayAreaAppearedInfo mForegroundApplicationsDisplay;
166     private DisplayAreaAppearedInfo mTitleBarDisplay;
167     private DisplayAreaAppearedInfo mVoicePlateDisplay;
168     private DisplayAreaAppearedInfo mBackgroundApplicationDisplay;
169     private DisplayAreaAppearedInfo mControlBarDisplay;
170     private DisplayAreaAppearedInfo mImeContainerDisplayArea;
171     private boolean mIsHostingDefaultApplicationDisplayAreaVisible;
172     private WindowManager mTitleBarWindowManager;
173     private View mTitleBarView;
174     private View mTitleHandleBarView;
175     private ImageView mImmersiveButtonView;
176     private Drawable mChevronUpDrawable;
177     private Drawable mChevronDownDrawable;
178     private boolean mIsForegroundDaVisible = false;
179     private boolean mIsForegroundDaFullScreen = false;
180     private boolean mIsForegroundAppRequestingImmersiveMode = false;
181     private boolean mIsUiModeNight = false;
182     private boolean mIsUserSetupInProgress;
183     private DisplayAreaComponent.FOREGROUND_DA_STATE mCurrentForegroundDaState;
184     // contains the list of activities that will be displayed on feature {@link
185     // CarDisplayAreaOrganizer.FEATURE_VOICE_PLATE)
186     private final Set<ComponentName> mVoicePlateActivitySet;
187     // true if there are activities still pending to be mapped to the voice plate DA as
188     // Car object was not created.
189     private boolean mIsPendingVoicePlateActivityMappingToDA;
190     private boolean mIsControlBarDisplayAreaEmpty = true;
191     private int mControlBarTaskId = -1;
192     private final CarServiceProvider mCarServiceProvider;
193     private Car mCar;
194 
195     /**
196      * The WindowContext that is registered with {@link #mTitleBarWindowManager} with options to
197      * specify the {@link RootDisplayArea} to attach the confirmation window.
198      */
199     @Nullable
200     private Context mTitleBarWindowContext;
201     private final TaskStackListener mOnActivityRestartAttemptListener = new TaskStackListener() {
202         @Override
203         public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
204                 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible)
205                 throws RemoteException {
206             super.onActivityRestartAttempt(task, homeTaskVisible, clearedTask, wasVisible);
207             logIfDebuggable("onActivityRestartAttempt: " + task);
208             updateForegroundDaVisibility(task);
209         }
210 
211         @Override
212         public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
213                 throws RemoteException {
214             super.onTaskMovedToFront(taskInfo);
215             logIfDebuggable("onTaskMovedToFront: " + taskInfo);
216             updateForegroundDaVisibility(taskInfo);
217         }
218 
219         @Override
220         public void onTaskRemoved(int taskId) throws RemoteException {
221             super.onTaskRemoved(taskId);
222             Log.e(TAG, " onTaskRemoved: " + taskId);
223             // maybe recover
224             if (mActiveTasksOnBackgroundDA != null
225                     && mActiveTasksOnBackgroundDA.isEmpty()) {
226                 // re launch background app
227                 relaunchBackgroundApp();
228             }
229 
230             if (mIsControlBarDisplayAreaEmpty && taskId == mControlBarTaskId) {
231                 relaunchControlBarApp();
232             }
233         }
234     };
235 
236     private final CarFullscreenTaskListener.OnTaskChangeListener mOnTaskChangeListener =
237             new CarFullscreenTaskListener.OnTaskChangeListener() {
238                 @Override
239                 public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
240                     logIfDebuggable("onTaskAppeared: " + taskInfo);
241                     updateForegroundDaVisibility(taskInfo);
242                     ComponentName componentName = null;
243                     if (taskInfo.baseIntent != null) {
244                         componentName = taskInfo.baseIntent.getComponent();
245                     }
246 
247                     boolean isBackgroundApp = mBackgroundActivityComponent.contains(componentName);
248                     if (isBackgroundApp) {
249                         addActiveTaskToBackgroundDAMap(taskInfo.taskId);
250                     }
251 
252                     boolean isControlBarApp = mControlBarActivityComponent.equals(componentName);
253                     if (isControlBarApp) {
254                         mIsControlBarDisplayAreaEmpty = false;
255                     }
256                 }
257 
258                 @Override
259                 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
260                     Log.e(TAG, " onTaskVanished: " + taskInfo);
261                     boolean isBackgroundApp = false;
262                     boolean isControlBarApp = false;
263                     ComponentName cmp = null;
264                     if (taskInfo.baseIntent != null) {
265                         cmp = taskInfo.baseIntent.getComponent();
266                         if (cmp != null) {
267                             isBackgroundApp = mBackgroundActivityComponent.contains(cmp);
268                             isControlBarApp = cmp.equals(mControlBarActivityComponent);
269                         }
270                     }
271 
272                     if (mActiveTasksOnBackgroundDA != null
273                             && mActiveTasksOnBackgroundDA.remove(taskInfo.taskId)) {
274                         logIfDebuggable("removed task " + taskInfo.taskId
275                                 + " from background DA, total tasks: "
276                                 + mActiveTasksOnBackgroundDA.size());
277                     }
278 
279                     if (isBackgroundApp && mActiveTasksOnBackgroundDA != null
280                             && mActiveTasksOnBackgroundDA.isEmpty()) {
281                         // re launch background app
282                         relaunchBackgroundApp();
283                     }
284 
285                     if (isControlBarApp) {
286                         // re launch controlbar app
287                         mIsControlBarDisplayAreaEmpty = true;
288                         relaunchControlBarApp();
289                     }
290 
291                     if (taskInfo.displayAreaFeatureId == FEATURE_VOICE_PLATE) {
292                         resetVoicePlateDisplayArea();
293                     }
294 
295                     if (mActiveTasksOnForegroundDA == null) {
296                         return;
297                     }
298 
299                     if (mActiveTasksOnForegroundDA.remove(taskInfo.taskId)) {
300                         logIfDebuggable("removed task " + taskInfo.taskId
301                                 + " from foreground DA, total tasks: "
302                                 + mActiveTasksOnForegroundDA.size());
303                     }
304 
305                     if (mActiveTasksOnForegroundDA.isEmpty()
306                             && isHostingDefaultApplicationDisplayAreaVisible()) {
307                         logIfDebuggable("no more tasks left in foreground DA, closing... ");
308                         startAnimation(CONTROL_BAR);
309                     }
310                 }
311             };
312 
313     private final CarUiPortraitDisplaySystemBarsController.Callback
314             mCarUiPortraitDisplaySystemBarsControllerCallback =
315             new CarUiPortraitDisplaySystemBarsController.Callback() {
316                 @Override
317                 public void onImmersiveRequestedChanged(ComponentName componentName,
318                         boolean requested) {
319                     // If the requesting application is a voice plate, background, or ignored
320                     // package, ignore immersive requests.
321                     if (mVoicePlateActivitySet != null && mVoicePlateActivitySet.contains(
322                             componentName)) {
323                         return;
324                     }
325                     if (mBackgroundActivityComponent != null
326                             && mBackgroundActivityComponent.contains(componentName)) {
327                         return;
328                     }
329                     if (mIgnoreOpeningForegroundDAComponentsSet != null
330                             && mIgnoreOpeningForegroundDAComponentsSet.contains(componentName)) {
331                         return;
332                     }
333 
334                     if (mTitleHandleBarView != null) {
335                         mTitleHandleBarView.setVisibility(requested ? View.GONE : View.VISIBLE);
336                     }
337                     if (mImmersiveButtonView != null) {
338                         mImmersiveButtonView.setVisibility(requested ? View.VISIBLE : View.GONE);
339                     }
340                     mIsForegroundAppRequestingImmersiveMode = requested;
341                 }
342 
343                 @Override
344                 public void onImmersiveStateChanged(boolean immersive) {
345                     setImmersive(immersive);
346                 }
347             };
348 
349     private final CarDeviceProvisionedListener mCarDeviceProvisionedListener =
350             new CarDeviceProvisionedListener() {
351                 @Override
352                 public void onUserSetupInProgressChanged() {
353                     updateUserSetupState();
354                 }
355     };
356 
relaunchBackgroundApp()357     private void relaunchBackgroundApp() {
358         logIfDebuggable("relaunching background app...");
359         Intent mapsIntent = new Intent();
360         mapsIntent.setComponent(mBackgroundActivityComponent.get(0));
361         mApplicationContext.startActivityAsUser(mapsIntent, UserHandle.CURRENT);
362     }
363 
relaunchControlBarApp()364     private void relaunchControlBarApp() {
365         logIfDebuggable("relaunching controlbar app...");
366         Intent controlBarIntent = new Intent();
367         controlBarIntent.setComponent(mControlBarActivityComponent);
368         mApplicationContext.startActivityAsUser(controlBarIntent,
369                 UserHandle.CURRENT);
370     }
371 
372     /**
373      * Initializes the controller
374      */
375     @Inject
CarDisplayAreaController(Context applicationContext, SyncTransactionQueue syncQueue, CarFullscreenTaskListener carFullscreenTaskListener, ShellExecutor shellExecutor, ConfigurationController configurationController, QSHost host, CarServiceProvider carServiceProvider, CarDisplayAreaOrganizer organizer, CarUiPortraitDisplaySystemBarsController carUiPortraitDisplaySystemBarsController, CommandQueue commandQueue, CarDeviceProvisionedController deviceProvisionedController)376     public CarDisplayAreaController(Context applicationContext, SyncTransactionQueue syncQueue,
377             CarFullscreenTaskListener carFullscreenTaskListener,
378             ShellExecutor shellExecutor,
379             ConfigurationController configurationController,
380             QSHost host,
381             CarServiceProvider carServiceProvider,
382             CarDisplayAreaOrganizer organizer,
383             CarUiPortraitDisplaySystemBarsController carUiPortraitDisplaySystemBarsController,
384             CommandQueue commandQueue,
385             CarDeviceProvisionedController deviceProvisionedController) {
386         mApplicationContext = applicationContext;
387         mSyncQueue = syncQueue;
388         mOrganizer = organizer;
389         mShellExecutor = shellExecutor;
390         mCarFullscreenTaskListener = carFullscreenTaskListener;
391         mConfigurationController = configurationController;
392         mCarServiceProvider = carServiceProvider;
393         mCarUiDisplaySystemBarsController = carUiPortraitDisplaySystemBarsController;
394         mCarDeviceProvisionedController = deviceProvisionedController;
395         mCarUiDisplaySystemBarsController.registerCallback(mApplicationContext.getDisplayId(),
396                 mCarUiPortraitDisplaySystemBarsControllerCallback);
397         mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
398         mConfigurationController.addCallback(this);
399         mDpiDensity = mOrganizer.getDpiDensity();
400         Resources resources = applicationContext.getResources();
401         mTotalScreenHeight = resources.getDimensionPixelSize(
402                 R.dimen.total_screen_height);
403         mTotalScreenWidth = resources.getDimensionPixelSize(
404                 R.dimen.total_screen_width);
405         mControlBarDisplayHeight = resources.getDimensionPixelSize(
406                 R.dimen.control_bar_height);
407         mFullDisplayHeight = resources.getDimensionPixelSize(
408                 R.dimen.full_app_display_area_height);
409         mDefaultDisplayHeight = resources.getDimensionPixelSize(
410                 R.dimen.default_app_display_area_height);
411         mChevronUpDrawable = resources.getDrawable(R.drawable.ic_chevron_up);
412         mChevronDownDrawable = resources.getDrawable(R.drawable.ic_chevron_down);
413         mCarDisplayAreaTouchHandler = new CarDisplayAreaTouchHandler(
414                 new HandlerExecutor(applicationContext.getMainThreadHandler()));
415         mControlBarActivityComponent = ComponentName.unflattenFromString(
416                 resources.getString(
417                         R.string.config_controlBarActivity));
418         mNotificationCenterComponent = ComponentName.unflattenFromString(resources.getString(
419                 R.string.config_notificationCenterActivity));
420         mHomeActivityComponent = ComponentName.unflattenFromString(resources.getString(
421                 R.string.config_homeActivity));
422         mBackgroundActivityComponent = new ArrayList<>();
423         mVoicePlateActivitySet = new ArraySet<>();
424         String[] backgroundActivities = mApplicationContext.getResources().getStringArray(
425                 R.array.config_backgroundActivities);
426         for (String backgroundActivity : backgroundActivities) {
427             mBackgroundActivityComponent
428                     .add(ComponentName.unflattenFromString(backgroundActivity));
429         }
430         mAssistUtils = new AssistUtils(applicationContext);
431         commandQueue.addCallback(this);
432 
433         // Get bottom nav bar height.
434         int navBarHeight = resources.getDimensionPixelSize(
435                 com.android.internal.R.dimen.navigation_bar_height);
436         if (navBarHeight > 0) {
437             mNavBarBounds.set(0, mTotalScreenHeight - navBarHeight, mTotalScreenWidth,
438                     mTotalScreenHeight);
439         }
440 
441         // Get left nav bar width.
442         int leftNavBarWidthResId = resources
443                 .getIdentifier("car_left_system_bar_width", "dimen", "android");
444         int leftNavBarWidth = 0;
445         if (leftNavBarWidthResId > 0) {
446             leftNavBarWidth = resources.getDimensionPixelSize(leftNavBarWidthResId);
447             mNavBarBounds.set(0, 0, leftNavBarWidth, mTotalScreenHeight);
448         }
449 
450         // Get right nav bar width.
451         int rightNavBarWidthResId = resources
452                 .getIdentifier("car_right_system_bar_width", "dimen", "android");
453         int rightNavBarWidth = 0;
454         if (rightNavBarWidthResId > 0) {
455             rightNavBarWidth = resources.getDimensionPixelSize(rightNavBarWidthResId);
456             mNavBarBounds.set(mTotalScreenWidth - rightNavBarWidth, 0, mTotalScreenWidth,
457                     mTotalScreenHeight);
458         }
459 
460         mScreenHeightWithoutNavBar = mTotalScreenHeight - mNavBarBounds.height();
461         mTitleBarHeight = resources.getDimensionPixelSize(R.dimen.title_bar_display_area_height);
462         mEnterExitAnimationDurationMs = resources.getInteger(
463                 R.integer.enter_exit_animation_foreground_display_area_duration_ms);
464         mTitleBarDragThreshold = resources.getDimensionPixelSize(
465                 R.dimen.title_bar_display_area_touch_drag_threshold);
466         mForegroundDisplayTop = mScreenHeightWithoutNavBar - mDefaultDisplayHeight;
467 
468         mForegroundDAComponentsVisibilityMap = new HashMap<>();
469         for (String component : mApplicationContext.getResources().getStringArray(
470                 R.array.config_foregroundDAComponents)) {
471             mForegroundDAComponentsVisibilityMap.put(component, false);
472         }
473 
474         String[] ignoreOpeningForegroundDACmp = mApplicationContext.getResources().getStringArray(
475                 R.array.config_ignoreOpeningForegroundDA);
476         mIgnoreOpeningForegroundDAComponentsSet = new ArraySet<>();
477         for (String component : ignoreOpeningForegroundDACmp) {
478             ComponentName componentName = ComponentName.unflattenFromString(component);
479             mIgnoreOpeningForegroundDAComponentsSet.add(componentName);
480         }
481     }
482 
483     @Override
animateExpandNotificationsPanel()484     public void animateExpandNotificationsPanel() {
485         String name = mNotificationCenterComponent.flattenToShortString();
486         if (isHostingDefaultApplicationDisplayAreaVisible()
487                 && mForegroundDAComponentsVisibilityMap.containsKey(name)
488                 && mForegroundDAComponentsVisibilityMap.get(name)) {
489             // notifications activity already visible
490             return;
491         }
492         Intent intent = new Intent();
493         intent.setComponent(mNotificationCenterComponent);
494         mApplicationContext.startActivityAsUser(intent, UserHandle.CURRENT);
495     }
496 
497     @Override
animateCollapsePanels(int flags, boolean force)498     public void animateCollapsePanels(int flags, boolean force) {
499         if (mIsForegroundDaFullScreen) {
500             return;
501         }
502         Intent homeActivityIntent = new Intent(Intent.ACTION_MAIN);
503         homeActivityIntent.addCategory(Intent.CATEGORY_HOME);
504         homeActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
505         mApplicationContext.startActivityAsUser(homeActivityIntent, UserHandle.CURRENT);
506     }
507 
508     /**
509      * Returns options that specify the {@link RootDisplayArea} to attach the confirmation window.
510      * {@code null} if the {@code rootDisplayAreaId} is {@link FEATURE_UNDEFINED}.
511      */
512     @Nullable
getOptionWithRootDisplayArea(int rootDisplayAreaId)513     private static Bundle getOptionWithRootDisplayArea(int rootDisplayAreaId) {
514         // In case we don't care which root display area the window manager is specifying.
515         if (rootDisplayAreaId == FEATURE_UNDEFINED) {
516             return null;
517         }
518 
519         Bundle options = new Bundle();
520         options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
521         return options;
522     }
523 
logIfDebuggable(String message)524     private static void logIfDebuggable(String message) {
525         if (DEBUG) {
526             Log.d(TAG, message);
527         }
528     }
529 
shouldIgnoreOpeningForegroundDA(ActivityManager.RunningTaskInfo taskInfo)530     boolean shouldIgnoreOpeningForegroundDA(ActivityManager.RunningTaskInfo taskInfo) {
531         return taskInfo.baseIntent != null && mIgnoreOpeningForegroundDAComponentsSet.contains(
532                 taskInfo.baseIntent.getComponent());
533     }
534 
setControlBarVisibility(boolean show)535     void setControlBarVisibility(boolean show) {
536         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
537         // Reset the layer for voice plate. This is needed as when the tasks are launched on
538         // other DA's those are brought to the top.
539         tx.setLayer(mControlBarDisplay.getLeash(), CONTROL_BAR_LAYER_INDEX);
540         if (show) {
541             tx.show(mControlBarDisplay.getLeash());
542         } else {
543             tx.hide(mControlBarDisplay.getLeash());
544         }
545         tx.apply(true);
546     }
547 
548     /**
549      * Show the title bar within a targeted display area using the rootDisplayAreaId.
550      */
showTitleBar()551     void showTitleBar() {
552         if (mTitleBarView != null) {
553             mTitleBarView.setVisibility(View.VISIBLE);
554             return;
555         }
556         hideTitleBar();
557         createTitleBar();
558     }
559 
createTitleBar()560     private void createTitleBar() {
561         LayoutInflater inflater = LayoutInflater.from(mApplicationContext);
562         mTitleBarView = inflater.inflate(R.layout.title_bar_display_area_view, null, true);
563         mTitleBarView.setVisibility(View.VISIBLE);
564         mTitleHandleBarView = mTitleBarView.findViewById(R.id.title_handle_bar);
565         mImmersiveButtonView = mTitleBarView.findViewById(R.id.immersive_button);
566         if (mImmersiveButtonView != null) {
567             mImmersiveButtonView.setImageDrawable(
568                     mIsForegroundDaFullScreen ? mChevronDownDrawable
569                             : mChevronUpDrawable);
570             mImmersiveButtonView.setOnClickListener(v -> {
571                 mCarUiDisplaySystemBarsController.requestImmersiveMode(
572                         mApplicationContext.getDisplayId(), !mIsForegroundDaFullScreen);
573             });
574         }
575 
576         // Show the confirmation.
577         WindowManager.LayoutParams lp = getTitleBarWindowLayoutParams();
578         getWindowManager().addView(mTitleBarView, lp);
579     }
580 
setImmersive(boolean immersive)581     private void setImmersive(boolean immersive) {
582         if (mIsForegroundDaFullScreen == immersive) {
583             return;
584         }
585         mIsForegroundDaFullScreen = immersive;
586         if (mIsForegroundDaFullScreen) {
587             if (!isForegroundDaVisible()) {
588                 makeForegroundDaVisible(true);
589             }
590             startAnimation(FULL);
591         } else {
592             startAnimation(FULL_TO_DEFAULT);
593         }
594         if (mImmersiveButtonView != null) {
595             mImmersiveButtonView.setImageDrawable(
596                     mIsForegroundDaFullScreen ? mChevronDownDrawable : mChevronUpDrawable);
597         }
598     }
599 
getWindowManager()600     private WindowManager getWindowManager() {
601         Bundle options = getOptionWithRootDisplayArea(FOREGROUND_DISPLAY_AREA_ROOT);
602         if (mTitleBarWindowManager == null || mTitleBarWindowContext == null) {
603             // Create window context to specify the RootDisplayArea
604             mTitleBarWindowContext = mApplicationContext.createWindowContext(
605                     TITLE_BAR_WINDOW_TYPE, options);
606             mTitleBarWindowManager = mTitleBarWindowContext.getSystemService(WindowManager.class);
607             return mTitleBarWindowManager;
608         }
609 
610         // Update the window context and window manager to specify the RootDisplayArea
611         IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
612         try {
613             wms.attachWindowContextToDisplayArea(mTitleBarWindowContext.getWindowContextToken(),
614                     TITLE_BAR_WINDOW_TYPE, mApplicationContext.getDisplayId(), options);
615         } catch (RemoteException e) {
616             throw e.rethrowAsRuntimeException();
617         }
618 
619         return mTitleBarWindowManager;
620     }
621 
622     /**
623      * Hide the title bar view
624      */
hideTitleBar()625     public void hideTitleBar() {
626         if (mTitleBarView != null) {
627             mTitleBarView.setVisibility(View.INVISIBLE);
628         }
629     }
630 
631     /**
632      * Remove the title bar view
633      */
removeTitleBar()634     public void removeTitleBar() {
635         if (mTitleBarView != null) {
636             mTitleBarWindowManager.removeView(mTitleBarView);
637         }
638     }
639 
getTitleBarWindowLayoutParams()640     private WindowManager.LayoutParams getTitleBarWindowLayoutParams() {
641         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
642                 ViewGroup.LayoutParams.MATCH_PARENT,
643                 mTitleBarHeight,
644                 TITLE_BAR_WINDOW_TYPE,
645                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
646                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
647                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
648                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
649                 PixelFormat.TRANSLUCENT);
650         lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.statusBars());
651         // Trusted overlay so touches outside the touchable area are allowed to pass through
652         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
653                 | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
654         lp.setTitle("TitleBar");
655         lp.gravity = Gravity.TOP;
656         lp.token = mWindowToken;
657         return lp;
658     }
659 
660     /**
661      * Returns if display area hosting default application is visible to user or not.
662      */
isHostingDefaultApplicationDisplayAreaVisible()663     public boolean isHostingDefaultApplicationDisplayAreaVisible() {
664         return mIsHostingDefaultApplicationDisplayAreaVisible;
665     }
666 
isDisplayAreaAnimating()667     boolean isDisplayAreaAnimating() {
668         return mOrganizer != null && mOrganizer.isDisplayAreaAnimating();
669     }
670 
671     /** Registers the DA organizer. */
register()672     public void register() {
673         logIfDebuggable("register organizer and set default bounds");
674 
675         ShellTaskOrganizer taskOrganizer = new ShellTaskOrganizer(mShellExecutor);
676         taskOrganizer.addListenerForType(mCarFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
677         // Use the same TaskListener for MULTI_WINDOW windowing mode as there is nothing that has
678         // to be done differently. This is because the tasks are still running in 'fullscreen'
679         // within a DisplayArea.
680         taskOrganizer.addListenerForType(mCarFullscreenTaskListener,
681                 TASK_LISTENER_TYPE_MULTI_WINDOW);
682 
683         taskOrganizer.registerOrganizer();
684         // Register DA organizer.
685         registerOrganizer();
686 
687         // Pre-calculate the foreground and background display bounds for different configs.
688         setDefaultBounds();
689 
690         // show the title bar window
691         showTitleBar();
692 
693         mCarDisplayAreaTouchHandler.registerOnClickListener((x, y) -> {
694             // Check if the click is outside the bounds of default display. If so, close the
695             // display area.
696             if (mIsHostingDefaultApplicationDisplayAreaVisible
697                     && y < (mForegroundDisplayTop)) {
698                 // TODO: closing logic goes here, something like: startAnimation(CONTROL_BAR);
699             }
700         });
701 
702         mCarDisplayAreaTouchHandler.registerTouchEventListener(
703                 new CarDisplayAreaTouchHandler.OnDragDisplayAreaListener() {
704 
705                     float mCurrentPos = -1;
706 
707                     @Override
708                     public void onStart(float x, float y) {
709                         mCurrentPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight
710                                 - mControlBarDisplayHeight;
711                     }
712 
713                     @Override
714                     public void onMove(float x, float y) {
715                         if (mIsForegroundAppRequestingImmersiveMode) {
716                             return;
717                         }
718                         if (y <= mScreenHeightWithoutNavBar - mDefaultDisplayHeight
719                                 - mControlBarDisplayHeight) {
720                             return;
721                         }
722                         animateToControlBarState((int) mCurrentPos, (int) y, 0);
723                         mCurrentPos = y;
724                     }
725 
726                     @Override
727                     public void onFinish(float x, float y) {
728                         if (mIsForegroundAppRequestingImmersiveMode) {
729                             return;
730                         }
731                         if (y >= mTitleBarDragThreshold) {
732                             animateToControlBarState((int) y,
733                                     mScreenHeightWithoutNavBar + mTitleBarHeight, 0);
734                             mCarDisplayAreaTouchHandler.updateTitleBarVisibility(false);
735                         } else {
736                             animateToDefaultState((int) y,
737                                     mScreenHeightWithoutNavBar - mDefaultDisplayHeight
738                                             - mControlBarDisplayHeight, 0);
739                         }
740                     }
741                 });
742         mCarDisplayAreaTouchHandler.enable(true);
743 
744         mCarServiceProvider.addListener(car -> {
745             mCar = car;
746             if (mIsPendingVoicePlateActivityMappingToDA) {
747                 mIsPendingVoicePlateActivityMappingToDA = false;
748                 updateVoicePlateActivityMap();
749             }
750         });
751 
752         ActivityTaskManager.getInstance().registerTaskStackListener(
753                 mOnActivityRestartAttemptListener);
754         // add CarFullscreenTaskListener to control the foreground DA when the task appears.
755         mCarFullscreenTaskListener.registerOnTaskChangeListener(mOnTaskChangeListener);
756 
757         updateUserSetupState();
758         mCarDeviceProvisionedController.addCallback(mCarDeviceProvisionedListener);
759     }
760 
updateVoicePlateActivityMap()761     void updateVoicePlateActivityMap() {
762         Context currentUserContext = mApplicationContext.createContextAsUser(
763                 UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0);
764 
765         Intent voiceIntent = new Intent(Intent.ACTION_VOICE_ASSIST, /* uri= */ null);
766         List<ResolveInfo> result = currentUserContext.getPackageManager().queryIntentActivities(
767                 voiceIntent, PackageManager.MATCH_ALL);
768         if (!result.isEmpty() && mCar == null) {
769             mIsPendingVoicePlateActivityMappingToDA = true;
770             return;
771         } else if (result.isEmpty()) {
772             return;
773         }
774 
775         CarActivityManager carAm = (CarActivityManager) mCar.getCarManager(
776                 Car.CAR_ACTIVITY_SERVICE);
777         for (ResolveInfo info : result) {
778             if (mVoicePlateActivitySet.add(info.activityInfo.getComponentName())) {
779                 logIfDebuggable("adding the following component to voice plate: "
780                         + info.activityInfo.getComponentName());
781                 CarDisplayAreaUtils.setPersistentActivity(carAm,
782                         info.activityInfo.getComponentName(),
783                         FEATURE_VOICE_PLATE, "VoicePlate");
784             }
785         }
786     }
787 
788     @Override
onConfigChanged(Configuration newConfig)789     public void onConfigChanged(Configuration newConfig) {
790         int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
791         if (currentNightMode == Configuration.UI_MODE_NIGHT_YES && !mIsUiModeNight) {
792             removeTitleBar();
793             mUiModeManager.setNightModeActivated(true);
794             createTitleBar();
795             mIsUiModeNight = true;
796         } else if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && mIsUiModeNight) {
797             removeTitleBar();
798             mUiModeManager.setNightModeActivated(false);
799             createTitleBar();
800             mIsUiModeNight = false;
801         }
802     }
803 
updateForegroundDaVisibility(ActivityManager.RunningTaskInfo taskInfo)804     private void updateForegroundDaVisibility(ActivityManager.RunningTaskInfo taskInfo) {
805         if (taskInfo.baseIntent == null || taskInfo.baseIntent.getComponent() == null
806                 || isDisplayAreaAnimating()) {
807             return;
808         }
809 
810         ComponentName componentName = taskInfo.baseIntent.getComponent();
811 
812         // Voice plate will be shown as the top most layer. Also, we don't want to change the
813         // state of the DA's when voice plate is shown.
814         boolean isVoicePlate = mVoicePlateActivitySet.contains(componentName);
815         if (isVoicePlate) {
816             showVoicePlateDisplayArea();
817             return;
818         }
819 
820         boolean isControlBar = componentName.equals(mControlBarActivityComponent);
821         boolean isBackgroundApp = mBackgroundActivityComponent.contains(componentName);
822         boolean isHomeActivity = componentName.equals(mHomeActivityComponent);
823 
824         if (isBackgroundApp) {
825             // we don't want to change the state of the foreground DA when background
826             // apps are launched.
827             return;
828         }
829 
830         if (isHomeActivity && (mCurrentForegroundDaState != CONTROL_BAR)) {
831             // close the foreground DA
832             startAnimation(CONTROL_BAR);
833             return;
834         }
835 
836         if (isControlBar) {
837             // we don't want to change the state of the foreground DA when
838             // controlbar apps are launched.
839             mControlBarTaskId = taskInfo.taskId;
840             return;
841         }
842 
843         if (mIsForegroundDaFullScreen) {
844             logIfDebuggable("foregroundDA in fullscreen mode, skip updating its state ");
845             return;
846         }
847 
848         // Check is there is an existing session running for assist, cancel it.
849         if (mAssistUtils.isSessionRunning()) {
850             mAssistUtils.hideCurrentSession();
851         }
852 
853         // Any task that does NOT meet all the below criteria should be ignored.
854         // 1. displayAreaFeatureId should be FEATURE_DEFAULT_TASK_CONTAINER
855         // 2. should be visible
856         // 3. for the current user ONLY. System user launches some tasks on cluster that should
857         //    not affect the state of the foreground DA
858         // 4. any task that is manually defined to be ignored
859         // 5. home activity. We use this activity as the wallpaper.
860         if (!(taskInfo.displayAreaFeatureId == FEATURE_DEFAULT_TASK_CONTAINER
861                 && taskInfo.isVisible()
862                 && taskInfo.userId == ActivityManager.getCurrentUser()
863                 && !shouldIgnoreOpeningForegroundDA(taskInfo)
864                 && !isHomeActivity)) {
865             return;
866         }
867 
868         String name = componentName.flattenToShortString();
869 
870         // check if the foreground DA is visible to the user
871         if (isHostingDefaultApplicationDisplayAreaVisible()) {
872             if (mForegroundDAComponentsVisibilityMap.containsKey(name)
873                     && mForegroundDAComponentsVisibilityMap.get(name)) {
874                 // close the foreground DA
875                 startAnimation(CONTROL_BAR);
876             }
877             addActiveTaskToForegroundDAMap(taskInfo.taskId);
878         } else {
879             logIfDebuggable("opening DA on request for cmp: " + componentName);
880             startAnimation(DEFAULT);
881             addActiveTaskToForegroundDAMap(taskInfo.taskId);
882         }
883 
884         mForegroundDAComponentsVisibilityMap.replaceAll((n, v) -> name.equals(n));
885     }
886 
addActiveTaskToForegroundDAMap(int taskId)887     private void addActiveTaskToForegroundDAMap(int taskId) {
888         if (mActiveTasksOnForegroundDA == null) {
889             mActiveTasksOnForegroundDA = new HashSet<>();
890         }
891         if (taskId != -1) {
892             mActiveTasksOnForegroundDA.add(taskId);
893             logIfDebuggable("added task to foreground DA: " + taskId + " total tasks: "
894                     + mActiveTasksOnForegroundDA.size());
895         }
896     }
897 
addActiveTaskToBackgroundDAMap(int taskId)898     private void addActiveTaskToBackgroundDAMap(int taskId) {
899         if (mActiveTasksOnBackgroundDA == null) {
900             mActiveTasksOnBackgroundDA = new HashSet<>();
901         }
902         if (taskId != -1) {
903             mActiveTasksOnBackgroundDA.add(taskId);
904             logIfDebuggable("added task to background DA: " + taskId + " total tasks: "
905                     + mActiveTasksOnBackgroundDA.size());
906 
907         }
908     }
909 
showVoicePlateDisplayArea()910     void showVoicePlateDisplayArea() {
911         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
912         // Reset the layer for voice plate. This is needed as when the tasks are launched on
913         // other DA's those are brought to the top.
914         tx.setLayer(mVoicePlateDisplay.getLeash(), VOICE_PLATE_LAYER_SHOWN_INDEX);
915         tx.show(mVoicePlateDisplay.getLeash());
916         tx.apply(true);
917     }
918 
resetVoicePlateDisplayArea()919     void resetVoicePlateDisplayArea() {
920         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
921         tx.hide(mVoicePlateDisplay.getLeash());
922         tx.apply(true);
923     }
924 
925     /** Registers DA organizer. */
registerOrganizer()926     private void registerOrganizer() {
927         List<DisplayAreaAppearedInfo> foregroundDisplayAreaInfos =
928                 mOrganizer.registerOrganizer(FOREGROUND_DISPLAY_AREA_ROOT);
929         if (foregroundDisplayAreaInfos.size() != 1) {
930             throw new IllegalStateException("Can't find display to launch default applications");
931         }
932 
933         List<DisplayAreaAppearedInfo> titleBarDisplayAreaInfo =
934                 mOrganizer.registerOrganizer(FEATURE_TITLE_BAR);
935         if (titleBarDisplayAreaInfo.size() != 1) {
936             throw new IllegalStateException("Can't find display to launch title bar");
937         }
938 
939         List<DisplayAreaAppearedInfo> voicePlateDisplayAreaInfo =
940                 mOrganizer.registerOrganizer(FEATURE_VOICE_PLATE);
941         if (voicePlateDisplayAreaInfo.size() != 1) {
942             throw new IllegalStateException("Can't find display to launch voice plate");
943         }
944 
945         List<DisplayAreaAppearedInfo> backgroundDisplayAreaInfos =
946                 mOrganizer.registerOrganizer(BACKGROUND_TASK_CONTAINER);
947         if (backgroundDisplayAreaInfos.size() != 1) {
948             throw new IllegalStateException("Can't find display to launch activity in background");
949         }
950 
951         List<DisplayAreaAppearedInfo> controlBarDisplayAreaInfos =
952                 mOrganizer.registerOrganizer(CONTROL_BAR_DISPLAY_AREA);
953         if (controlBarDisplayAreaInfos.size() != 1) {
954             throw new IllegalStateException("Can't find display to launch audio control");
955         }
956 
957         // Get the IME display area attached to the root hierarchy.
958         List<DisplayAreaAppearedInfo> imeDisplayAreaInfos =
959                 mOrganizer.registerOrganizer(FEATURE_IME_PLACEHOLDER);
960         for (DisplayAreaAppearedInfo info : imeDisplayAreaInfos) {
961             DisplayAreaInfo daInfo = info.getDisplayAreaInfo();
962             // Need to check the display for the multi displays platform.
963             if (daInfo.rootDisplayAreaId == FEATURE_ROOT
964                     && daInfo.displayId == Display.DEFAULT_DISPLAY) {
965                 mImeContainerDisplayArea = info;
966             }
967         }
968         // As we have only 1 display defined for each display area feature get the 0th index.
969         mForegroundApplicationsDisplay = foregroundDisplayAreaInfos.get(0);
970         mTitleBarDisplay = titleBarDisplayAreaInfo.get(0);
971         mVoicePlateDisplay = voicePlateDisplayAreaInfo.get(0);
972         mBackgroundApplicationDisplay = backgroundDisplayAreaInfos.get(0);
973         mControlBarDisplay = controlBarDisplayAreaInfos.get(0);
974 
975         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
976         // TODO(b/188102153): replace to set mForegroundApplicationsDisplay to top.
977         tx.setLayer(mBackgroundApplicationDisplay.getLeash(), BACKGROUND_LAYER_INDEX);
978         tx.setLayer(mForegroundApplicationsDisplay.getLeash(), FOREGROUND_LAYER_INDEX);
979         tx.setLayer(mTitleBarDisplay.getLeash(), TITLE_BAR_LAYER_INDEX);
980         tx.setLayer(mVoicePlateDisplay.getLeash(), VOICE_PLATE_LAYER_SHOWN_INDEX);
981         tx.setLayer(mControlBarDisplay.getLeash(), CONTROL_BAR_LAYER_INDEX);
982 
983         tx.hide(mVoicePlateDisplay.getLeash());
984         tx.hide(mForegroundApplicationsDisplay.getLeash());
985         tx.apply(true);
986     }
987 
988     /** Un-Registers DA organizer. */
unregister()989     public void unregister() {
990         mOrganizer.resetWindowsOffset();
991         mOrganizer.unregisterOrganizer();
992         mForegroundApplicationsDisplay = null;
993         mTitleBarDisplay = null;
994         mBackgroundApplicationDisplay = null;
995         mControlBarDisplay = null;
996         mVoicePlateDisplay = null;
997         mImeContainerDisplayArea = null;
998         mCarDisplayAreaTouchHandler.enable(false);
999         ActivityTaskManager.getInstance()
1000                 .unregisterTaskStackListener(mOnActivityRestartAttemptListener);
1001         mCarDeviceProvisionedController.removeCallback(mCarDeviceProvisionedListener);
1002         mTitleBarView.setVisibility(View.GONE);
1003     }
1004 
1005     /**
1006      * This method should be called after the registration of DA's are done. The method expects a
1007      * target state as an argument, according to which the animations will take place. For example,
1008      * if the target state is {@link DisplayAreaComponent.FOREGROUND_DA_STATE#DEFAULT} then the
1009      * foreground DA hosting default applications will animate to the default set height.
1010      */
startAnimation(DisplayAreaComponent.FOREGROUND_DA_STATE toState)1011     public void startAnimation(DisplayAreaComponent.FOREGROUND_DA_STATE toState) {
1012         if (mIsUserSetupInProgress) {
1013             // No animations while in setup
1014             return;
1015         }
1016         // TODO: currently the animations are only bottom/up. Make it more generic animations here.
1017         int fromPos = 0;
1018         int toPos = 0;
1019         mCurrentForegroundDaState = toState;
1020 
1021         switch (toState) {
1022             case CONTROL_BAR:
1023                 // Foreground DA closes.
1024                 fromPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight
1025                         - mControlBarDisplayHeight;
1026                 toPos = mScreenHeightWithoutNavBar + mTitleBarHeight;
1027                 animateToControlBarState(fromPos, toPos, mEnterExitAnimationDurationMs);
1028                 mCarDisplayAreaTouchHandler.updateTitleBarVisibility(false);
1029                 break;
1030             case FULL:
1031                 fromPos =
1032                         isForegroundDaVisible() ? mScreenHeightWithoutNavBar - mDefaultDisplayHeight
1033                                 - mControlBarDisplayHeight
1034                                 : mScreenHeightWithoutNavBar + mTitleBarHeight;
1035                 toPos = mTitleBarHeight;
1036                 animateToFullState(fromPos, toPos, mEnterExitAnimationDurationMs);
1037                 break;
1038             case FULL_TO_DEFAULT:
1039                 toPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight
1040                         - mControlBarDisplayHeight;
1041                 animateFullToDefaultState(fromPos, toPos, mEnterExitAnimationDurationMs);
1042                 break;
1043             default:
1044                 // Foreground DA opens to default height.
1045                 // update the bounds to expand the foreground display area before starting
1046                 // animations.
1047                 fromPos = mScreenHeightWithoutNavBar + mTitleBarHeight;
1048                 toPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight
1049                         - mControlBarDisplayHeight;
1050                 animateToDefaultState(fromPos, toPos, mEnterExitAnimationDurationMs);
1051         }
1052     }
1053 
animateToControlBarState(int fromPos, int toPos, int durationMs)1054     private void animateToControlBarState(int fromPos, int toPos, int durationMs) {
1055         mBackgroundApplicationDisplayBounds.bottom =
1056                 mScreenHeightWithoutNavBar - mControlBarDisplayHeight;
1057         animate(fromPos, toPos, CONTROL_BAR, durationMs);
1058         mIsHostingDefaultApplicationDisplayAreaVisible = false;
1059         broadcastForegroundDAVisibilityChange(false);
1060     }
1061 
animateToDefaultState(int fromPos, int toPos, int durationMs)1062     private void animateToDefaultState(int fromPos, int toPos, int durationMs) {
1063         if (!isForegroundDaVisible()) {
1064             makeForegroundDaVisible(true);
1065             showTitleBar();
1066         }
1067         mBackgroundApplicationDisplayBounds.bottom = toPos - mTitleBarHeight;
1068         animate(fromPos, toPos, DEFAULT, durationMs);
1069         mIsHostingDefaultApplicationDisplayAreaVisible = true;
1070         broadcastForegroundDAVisibilityChange(true);
1071         if (mCarDisplayAreaTouchHandler != null) {
1072             mCarDisplayAreaTouchHandler.updateTitleBarVisibility(true);
1073         }
1074     }
1075 
animateFullToDefaultState(int fromPos, int toPos, int durationMs)1076     private void animateFullToDefaultState(int fromPos, int toPos, int durationMs) {
1077         mBackgroundApplicationDisplayBounds.bottom = toPos - mTitleBarHeight;
1078         mIsForegroundDaFullScreen = false;
1079         animate(fromPos, toPos, FULL_TO_DEFAULT, durationMs);
1080         mIsHostingDefaultApplicationDisplayAreaVisible = true;
1081         showTitleBar();
1082         setControlBarVisibility(true);
1083         if (mCarDisplayAreaTouchHandler != null) {
1084             mCarDisplayAreaTouchHandler.updateTitleBarVisibility(true);
1085         }
1086     }
1087 
animateToFullState(int fromPos, int toPos, int durationMs)1088     private void animateToFullState(int fromPos, int toPos, int durationMs) {
1089         if (!isForegroundDaVisible()) {
1090             makeForegroundDaVisible(true);
1091         }
1092         setControlBarVisibility(false);
1093         mBackgroundApplicationDisplayBounds.bottom = mTotalScreenHeight;
1094         makeForegroundDAFullScreen(/* setFullPosition= */ false, /* showTitleBar= */ true);
1095         animate(fromPos, toPos, FULL, durationMs);
1096         mIsHostingDefaultApplicationDisplayAreaVisible = true;
1097         if (mCarDisplayAreaTouchHandler != null) {
1098             mCarDisplayAreaTouchHandler.updateTitleBarVisibility(false);
1099         }
1100     }
1101 
animate(int fromPos, int toPos, DisplayAreaComponent.FOREGROUND_DA_STATE toState, int durationMs)1102     private void animate(int fromPos, int toPos, DisplayAreaComponent.FOREGROUND_DA_STATE toState,
1103             int durationMs) {
1104         if (mOrganizer != null) {
1105             mOrganizer.scheduleOffset(fromPos, toPos, mBackgroundApplicationDisplayBounds,
1106                     mForegroundApplicationDisplayBounds, mBackgroundApplicationDisplay,
1107                     mForegroundApplicationsDisplay, mControlBarDisplay, toState, durationMs);
1108         }
1109     }
1110 
makeForegroundDaVisible(boolean isVisible)1111     void makeForegroundDaVisible(boolean isVisible) {
1112         logIfDebuggable("make foregroundDA visible? " + isVisible);
1113         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
1114         if (isVisible) {
1115             tx.show(mForegroundApplicationsDisplay.getLeash());
1116             mIsForegroundDaVisible = true;
1117         } else {
1118             tx.hide(mForegroundApplicationsDisplay.getLeash());
1119             mIsForegroundDaVisible = false;
1120         }
1121         tx.apply(true);
1122     }
1123 
isForegroundDaVisible()1124     boolean isForegroundDaVisible() {
1125         return mIsForegroundDaVisible;
1126     }
1127 
1128     /** Pre-calculates the display bounds for different DA's. */
setDefaultBounds()1129     void setDefaultBounds() {
1130         logIfDebuggable("setting default bounds for all the DA's");
1131         int controlBarTop = mScreenHeightWithoutNavBar - mControlBarDisplayHeight;
1132         int foregroundTop =
1133                 mScreenHeightWithoutNavBar - mDefaultDisplayHeight - mControlBarDisplayHeight;
1134 
1135         // Bottom nav bar. Bottom nav bar height will be 0 if the nav bar is present on the sides.
1136         Rect backgroundBounds = new Rect(0, 0, mTotalScreenWidth, controlBarTop);
1137         Rect controlBarBounds = new Rect(0, controlBarTop, mTotalScreenWidth,
1138                 mScreenHeightWithoutNavBar);
1139         Rect foregroundBounds = new Rect(0,
1140                 foregroundTop, mTotalScreenWidth,
1141                 mScreenHeightWithoutNavBar - mControlBarDisplayHeight);
1142         Rect voicePlateBounds = new Rect(0, 0, mTotalScreenWidth,
1143                 mScreenHeightWithoutNavBar - mControlBarDisplayHeight);
1144         Rect titleBarBounds = new Rect(0,
1145                 foregroundTop - mTitleBarHeight, mTotalScreenWidth, foregroundTop);
1146 
1147         // Adjust the bounds based on the nav bar.
1148         // TODO: account for the case where nav bar is at the top.
1149 
1150         // Populate the bounds depending on where the nav bar is.
1151         if (mNavBarBounds.left == 0 && mNavBarBounds.top == 0) {
1152             // Left nav bar.
1153             backgroundBounds.left = mNavBarBounds.right;
1154             controlBarBounds.left = mNavBarBounds.right;
1155             foregroundBounds.left = mNavBarBounds.right;
1156             titleBarBounds.left = mNavBarBounds.right;
1157         } else if (mNavBarBounds.top == 0) {
1158             // Right nav bar.
1159             backgroundBounds.right = mNavBarBounds.left;
1160             controlBarBounds.right = mNavBarBounds.left;
1161             foregroundBounds.right = mNavBarBounds.left;
1162             titleBarBounds.right = mNavBarBounds.left;
1163         }
1164 
1165         mBackgroundApplicationDisplayBounds.set(backgroundBounds);
1166         mControlBarDisplayBounds.set(controlBarBounds);
1167         mForegroundApplicationDisplayBounds.set(foregroundBounds);
1168         mTitleBarDisplayBounds.set(titleBarBounds);
1169         mVoicePlateDisplayBounds.set(voicePlateBounds);
1170         mCarDisplayAreaTouchHandler.setTitleBarBounds(titleBarBounds);
1171 
1172         // Set the initial bounds for first and second displays.
1173         updateBounds();
1174         mIsForegroundDaFullScreen = false;
1175     }
1176 
1177     /** Updates the default and background display bounds for the given config. */
updateBounds()1178     private void updateBounds() {
1179         WindowContainerTransaction wct = new WindowContainerTransaction();
1180 
1181         Rect foregroundApplicationDisplayBound = mForegroundApplicationDisplayBounds;
1182         Rect titleBarDisplayBounds = mTitleBarDisplayBounds;
1183         Rect voicePlateDisplayBounds = mVoicePlateDisplayBounds;
1184         Rect backgroundApplicationDisplayBound = mBackgroundApplicationDisplayBounds;
1185         Rect controlBarDisplayBound = mControlBarDisplayBounds;
1186 
1187         WindowContainerToken foregroundDisplayToken =
1188                 mForegroundApplicationsDisplay.getDisplayAreaInfo().token;
1189         WindowContainerToken imeRootDisplayToken =
1190                 mImeContainerDisplayArea.getDisplayAreaInfo().token;
1191         WindowContainerToken titleBarDisplayToken =
1192                 mTitleBarDisplay.getDisplayAreaInfo().token;
1193         WindowContainerToken voicePlateDisplayToken =
1194                 mVoicePlateDisplay.getDisplayAreaInfo().token;
1195         WindowContainerToken backgroundDisplayToken =
1196                 mBackgroundApplicationDisplay.getDisplayAreaInfo().token;
1197         WindowContainerToken controlBarDisplayToken =
1198                 mControlBarDisplay.getDisplayAreaInfo().token;
1199 
1200         // Default TDA
1201         int foregroundDisplayWidthDp =
1202                 foregroundApplicationDisplayBound.width() * DisplayMetrics.DENSITY_DEFAULT
1203                         / mDpiDensity;
1204         int foregroundDisplayHeightDp =
1205                 foregroundApplicationDisplayBound.height() * DisplayMetrics.DENSITY_DEFAULT
1206                         / mDpiDensity;
1207         wct.setBounds(foregroundDisplayToken, foregroundApplicationDisplayBound);
1208         wct.setScreenSizeDp(foregroundDisplayToken, foregroundDisplayWidthDp,
1209                 foregroundDisplayHeightDp);
1210         wct.setSmallestScreenWidthDp(foregroundDisplayToken, foregroundDisplayWidthDp);
1211 
1212         // Title bar
1213         int titleBarDisplayWidthDp =
1214                 titleBarDisplayBounds.width() * DisplayMetrics.DENSITY_DEFAULT
1215                         / mDpiDensity;
1216         int titleBarDisplayHeightDp =
1217                 titleBarDisplayBounds.height() * DisplayMetrics.DENSITY_DEFAULT
1218                         / mDpiDensity;
1219         wct.setBounds(titleBarDisplayToken, titleBarDisplayBounds);
1220         wct.setScreenSizeDp(titleBarDisplayToken, titleBarDisplayWidthDp,
1221                 titleBarDisplayHeightDp);
1222         wct.setSmallestScreenWidthDp(titleBarDisplayToken, titleBarDisplayWidthDp);
1223 
1224         // voice plate
1225         int voicePlateDisplayWidthDp =
1226                 voicePlateDisplayBounds.width() * DisplayMetrics.DENSITY_DEFAULT
1227                         / mDpiDensity;
1228         int voicePlateDisplayHeightDp =
1229                 voicePlateDisplayBounds.height() * DisplayMetrics.DENSITY_DEFAULT
1230                         / mDpiDensity;
1231         wct.setBounds(voicePlateDisplayToken, voicePlateDisplayBounds);
1232         wct.setScreenSizeDp(voicePlateDisplayToken, voicePlateDisplayWidthDp,
1233                 voicePlateDisplayHeightDp);
1234         wct.setSmallestScreenWidthDp(voicePlateDisplayToken, voicePlateDisplayWidthDp);
1235 
1236         // background TDA
1237         int backgroundDisplayWidthDp =
1238                 backgroundApplicationDisplayBound.width() * DisplayMetrics.DENSITY_DEFAULT
1239                         / mDpiDensity;
1240         int backgroundDisplayHeightDp =
1241                 backgroundApplicationDisplayBound.height() * DisplayMetrics.DENSITY_DEFAULT
1242                         / mDpiDensity;
1243         wct.setBounds(backgroundDisplayToken, backgroundApplicationDisplayBound);
1244         wct.setScreenSizeDp(backgroundDisplayToken, backgroundDisplayWidthDp,
1245                 backgroundDisplayHeightDp);
1246         wct.setSmallestScreenWidthDp(backgroundDisplayToken, backgroundDisplayWidthDp);
1247 
1248         // Change the bounds of the IME attached to the root display to be same as the background DA
1249         wct.setBounds(imeRootDisplayToken, backgroundApplicationDisplayBound);
1250         wct.setScreenSizeDp(imeRootDisplayToken, backgroundDisplayWidthDp,
1251                 backgroundDisplayHeightDp);
1252         wct.setSmallestScreenWidthDp(imeRootDisplayToken, backgroundDisplayWidthDp);
1253 
1254         // control bar
1255         int controlBarDisplayWidthDp =
1256                 controlBarDisplayBound.width() * DisplayMetrics.DENSITY_DEFAULT
1257                         / mDpiDensity;
1258         int controlBarDisplayHeightDp =
1259                 controlBarDisplayBound.height() * DisplayMetrics.DENSITY_DEFAULT
1260                         / mDpiDensity;
1261         wct.setBounds(controlBarDisplayToken, controlBarDisplayBound);
1262         wct.setScreenSizeDp(controlBarDisplayToken, controlBarDisplayWidthDp,
1263                 controlBarDisplayHeightDp);
1264         wct.setSmallestScreenWidthDp(controlBarDisplayToken, controlBarDisplayWidthDp);
1265         mSyncQueue.queue(wct);
1266 
1267         mSyncQueue.runInSync(t -> {
1268             Rect foregroundApplicationAndTitleBarDisplayBound = new Rect(0, -mTitleBarHeight,
1269                     foregroundApplicationDisplayBound.width(),
1270                     foregroundApplicationDisplayBound.height());
1271             t.setCrop(mForegroundApplicationsDisplay.getLeash(),
1272                     foregroundApplicationAndTitleBarDisplayBound);
1273             t.setPosition(mForegroundApplicationsDisplay.getLeash(),
1274                     foregroundApplicationDisplayBound.left,
1275                     foregroundApplicationDisplayBound.top);
1276 
1277             t.setWindowCrop(mVoicePlateDisplay.getLeash(),
1278                     voicePlateDisplayBounds.width(), voicePlateDisplayBounds.height());
1279             t.setPosition(mVoicePlateDisplay.getLeash(),
1280                     voicePlateDisplayBounds.left,
1281                     voicePlateDisplayBounds.top);
1282 
1283             t.setWindowCrop(mTitleBarDisplay.getLeash(),
1284                     titleBarDisplayBounds.width(), titleBarDisplayBounds.height());
1285             t.setPosition(mTitleBarDisplay.getLeash(),
1286                     titleBarDisplayBounds.left, -mTitleBarHeight);
1287 
1288             t.setWindowCrop(mBackgroundApplicationDisplay.getLeash(),
1289                     backgroundApplicationDisplayBound.width(),
1290                     backgroundApplicationDisplayBound.height());
1291             t.setPosition(mBackgroundApplicationDisplay.getLeash(),
1292                     backgroundApplicationDisplayBound.left,
1293                     backgroundApplicationDisplayBound.top);
1294 
1295             t.setWindowCrop(mImeContainerDisplayArea.getLeash(),
1296                     backgroundApplicationDisplayBound.width(),
1297                     backgroundApplicationDisplayBound.height());
1298             t.setPosition(mImeContainerDisplayArea.getLeash(),
1299                     backgroundApplicationDisplayBound.left,
1300                     backgroundApplicationDisplayBound.top);
1301 
1302             t.setWindowCrop(mControlBarDisplay.getLeash(),
1303                     controlBarDisplayBound.width(), controlBarDisplayBound.height());
1304             t.setPosition(mControlBarDisplay.getLeash(),
1305                     controlBarDisplayBound.left,
1306                     controlBarDisplayBound.top);
1307         });
1308     }
1309 
1310     /** Bypass the typical fullscreen flow specifically for SUW */
immersiveForSUW(boolean immersive)1311     void immersiveForSUW(boolean immersive) {
1312         if (immersive) {
1313             makeForegroundDAFullScreen(/* setFullPosition= */ true, /* showTitleBar= */ false);
1314         } else {
1315             setDefaultBounds();
1316         }
1317         mCarUiDisplaySystemBarsController.requestImmersiveModeForSUW(
1318                 mApplicationContext.getDisplayId(), immersive);
1319     }
1320 
1321     /**
1322      * Update the bounds of foreground DA to cover full screen.
1323      *
1324      * @param setFullPosition whether or not the surface's position should be set to the full
1325      *                        position. Setting this to true will set the position to the full
1326      *                        screen while setting to false will use the default display bounds.
1327      */
makeForegroundDAFullScreen(boolean setFullPosition, boolean showTitleBar)1328     void makeForegroundDAFullScreen(boolean setFullPosition, boolean showTitleBar) {
1329         logIfDebuggable("make foregroundDA fullscreen");
1330         WindowContainerTransaction wct = new WindowContainerTransaction();
1331         int topBound = showTitleBar ? mTitleBarHeight : 0;
1332         Rect foregroundApplicationDisplayBounds = new Rect(0, topBound, mTotalScreenWidth,
1333                 mTotalScreenHeight);
1334         WindowContainerToken foregroundDisplayToken =
1335                 mForegroundApplicationsDisplay.getDisplayAreaInfo().token;
1336 
1337         int foregroundDisplayWidthDp =
1338                 foregroundApplicationDisplayBounds.width() * DisplayMetrics.DENSITY_DEFAULT
1339                         / mDpiDensity;
1340         int foregroundDisplayHeightDp =
1341                 foregroundApplicationDisplayBounds.height() * DisplayMetrics.DENSITY_DEFAULT
1342                         / mDpiDensity;
1343         wct.setBounds(foregroundDisplayToken, foregroundApplicationDisplayBounds);
1344         wct.setScreenSizeDp(foregroundDisplayToken, foregroundDisplayWidthDp,
1345                 foregroundDisplayHeightDp);
1346         wct.setSmallestScreenWidthDp(foregroundDisplayToken,
1347                 Math.min(foregroundDisplayWidthDp, foregroundDisplayHeightDp));
1348         mSyncQueue.queue(wct);
1349 
1350         mSyncQueue.runInSync(t -> {
1351             Rect foregroundApplicationAndTitleBarDisplayBound = new Rect(0, -topBound,
1352                     foregroundApplicationDisplayBounds.width(),
1353                     foregroundApplicationDisplayBounds.height());
1354             t.setWindowCrop(mForegroundApplicationsDisplay.getLeash(),
1355                     foregroundApplicationAndTitleBarDisplayBound);
1356             if (setFullPosition) {
1357                 t.setPosition(mForegroundApplicationsDisplay.getLeash(), 0, 0);
1358             }
1359         });
1360 
1361         mIsForegroundDaFullScreen = true;
1362     }
1363 
broadcastForegroundDAVisibilityChange(boolean visible)1364     private void broadcastForegroundDAVisibilityChange(boolean visible) {
1365         Intent intent = new Intent(DISPLAY_AREA_VISIBILITY_CHANGED);
1366         intent.putExtra(INTENT_EXTRA_IS_DISPLAY_AREA_VISIBLE, visible);
1367         LocalBroadcastManager.getInstance(mApplicationContext).sendBroadcast(
1368                 intent);
1369     }
1370 
updateUserSetupState()1371     private void updateUserSetupState() {
1372         boolean userSetupInProgress = mCarDeviceProvisionedController
1373                 .isCurrentUserSetupInProgress();
1374         if (mIsUserSetupInProgress == userSetupInProgress) {
1375             return;
1376         }
1377         mIsUserSetupInProgress = userSetupInProgress;
1378         if (mIsUserSetupInProgress) {
1379             if (!isForegroundDaVisible()) {
1380                 hideTitleBar();
1381                 makeForegroundDaVisible(true);
1382             }
1383             setControlBarVisibility(false);
1384             immersiveForSUW(true);
1385         } else {
1386             makeForegroundDaVisible(false);
1387             immersiveForSUW(false);
1388             showTitleBar();
1389             setControlBarVisibility(true);
1390         }
1391     }
1392 }
1393