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.car.carlauncher.displayarea; 18 19 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; 20 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; 21 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; 22 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; 23 24 import static com.android.car.carlauncher.AppGridActivity.CAR_LAUNCHER_STATE.CONTROL_BAR; 25 import static com.android.car.carlauncher.AppGridActivity.CAR_LAUNCHER_STATE.DEFAULT; 26 import static com.android.car.carlauncher.displayarea.CarDisplayAreaOrganizer.BACKGROUND_TASK_CONTAINER; 27 import static com.android.car.carlauncher.displayarea.CarDisplayAreaOrganizer.CONTROL_BAR_DISPLAY_AREA; 28 import static com.android.car.carlauncher.displayarea.CarDisplayAreaOrganizer.FEATURE_TITLE_BAR; 29 import static com.android.car.carlauncher.displayarea.CarDisplayAreaOrganizer.FEATURE_VOICE_PLATE; 30 import static com.android.car.carlauncher.displayarea.CarDisplayAreaOrganizer.FOREGROUND_DISPLAY_AREA_ROOT; 31 import static com.android.car.carlauncher.displayarea.CarFullscreenTaskListener.MAPS; 32 33 import android.app.ActivityManager; 34 import android.app.ActivityTaskManager; 35 import android.app.TaskStackListener; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.res.Resources; 40 import android.graphics.PixelFormat; 41 import android.graphics.Rect; 42 import android.os.Binder; 43 import android.os.Bundle; 44 import android.os.IBinder; 45 import android.os.RemoteException; 46 import android.os.UserHandle; 47 import android.util.ArraySet; 48 import android.util.DisplayMetrics; 49 import android.view.Gravity; 50 import android.view.IWindowManager; 51 import android.view.LayoutInflater; 52 import android.view.SurfaceControl; 53 import android.view.View; 54 import android.view.ViewGroup; 55 import android.view.WindowInsets; 56 import android.view.WindowManager; 57 import android.view.WindowManagerGlobal; 58 import android.window.DisplayAreaAppearedInfo; 59 import android.window.WindowContainerToken; 60 import android.window.WindowContainerTransaction; 61 62 import androidx.annotation.Nullable; 63 64 import com.android.car.carlauncher.AppGridActivity; 65 import com.android.car.carlauncher.ControlBarActivity; 66 import com.android.car.carlauncher.R; 67 import com.android.internal.app.AssistUtils; 68 import com.android.wm.shell.common.SyncTransactionQueue; 69 70 import java.util.HashMap; 71 import java.util.List; 72 73 /** 74 * Controls the bounds of the home background and application displays. This is a singleton class as 75 * there should be one controller used to register and control the DA's 76 */ 77 public class CarDisplayAreaController { 78 private static final String TAG = "CarDisplayAreaController"; 79 // TODO(b/198783542): Make this configurable. 80 private static final String LOCATION_SETTINGS_ACTIVITY = "LocationSettingsCheckerAutoActivity"; 81 private static final String GRANT_PERMISSION_ACTIVITY = "GrantPermissionsActivity"; 82 // TODO(b/194334719): Remove when display area logic is moved into systemui 83 private static final String DISPLAY_AREA_VISIBILITY_CHANGED = 84 "com.android.car.carlauncher.displayarea.DISPLAY_AREA_VISIBILITY_CHANGED"; 85 private static final String INTENT_EXTRA_IS_DISPLAY_AREA_VISIBLE = 86 "EXTRA_IS_DISPLAY_AREA_VISIBLE"; 87 88 // Layer index of how display areas should be placed. Keeping a gap of 100 if we want to 89 // add some other display area layers in between in future. 90 static final int BACKGROUND_LAYER_INDEX = 200; 91 static final int FOREGROUND_LAYER_INDEX = 100; 92 static final int VOICE_PLATE_LAYER_SHOWN_INDEX = 500; 93 static final int CONTROL_BAR_LAYER_INDEX = 0; 94 static final CarDisplayAreaController INSTANCE = new CarDisplayAreaController(); 95 private static final int TITLE_BAR_WINDOW_TYPE = 96 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 97 98 private final Rect mControlBarDisplayBounds = new Rect(); 99 private final Rect mForegroundApplicationDisplayBounds = new Rect(); 100 private final Rect mTitleBarDisplayBounds = new Rect(); 101 private final Rect mVoicePlateDisplayBounds = new Rect(); 102 private final Rect mBackgroundApplicationDisplayBounds = new Rect(); 103 private final Rect mNavBarBounds = new Rect(); 104 private final IBinder mWindowToken = new Binder(); 105 106 private SyncTransactionQueue mSyncQueue; 107 private CarDisplayAreaOrganizer mOrganizer; 108 private DisplayAreaAppearedInfo mForegroundApplicationsDisplay; 109 private DisplayAreaAppearedInfo mTitleBarDisplay; 110 private DisplayAreaAppearedInfo mVoicePlateDisplay; 111 private DisplayAreaAppearedInfo mBackgroundApplicationDisplay; 112 private DisplayAreaAppearedInfo mControlBarDisplay; 113 private DisplayAreaAppearedInfo mImeContainerDisplayArea; 114 private String mControlBarActivityComponent; 115 private HashMap<String, Boolean> mForegroundDAComponentsVisibilityMap; 116 private ArraySet<ComponentName> mIgnoreOpeningForegroundDAComponentsSet; 117 private int mTitleBarDragThreshold; 118 private int mEnterExitAnimationDurationMs; 119 private int mDpiDensity; 120 private int mTotalScreenWidth = -1; 121 // height of DA hosting the control bar. 122 private int mControlBarDisplayHeight; 123 // height of DA hosting default apps and covering the maps fully. 124 private int mFullDisplayHeight; 125 // height of DA hosting default apps and covering the maps to default height. 126 private int mDefaultDisplayHeight; 127 private int mTitleBarHeight; 128 private int mScreenHeightWithoutNavBar; 129 private int mTotalScreenHeight; 130 private boolean mIsHostingDefaultApplicationDisplayAreaVisible; 131 private CarDisplayAreaTouchHandler mCarDisplayAreaTouchHandler; 132 133 private WindowManager mTitleBarWindowManager; 134 private View mTitleBarView; 135 private Context mApplicationContext; 136 private int mForegroundDisplayTop; 137 private AssistUtils mAssistUtils; 138 private boolean mIsForegroundDaVisible = false; 139 140 /** 141 * The WindowContext that is registered with {@link #mTitleBarWindowManager} with options to 142 * specify the {@link RootDisplayArea} to attach the confirmation window. 143 */ 144 @Nullable 145 private Context mTitleBarWindowContext; 146 private boolean mIsGridViewVisibleInForegroundDisplayArea; 147 private ComponentName mAssistantVoicePlateActivityName; 148 149 private final TaskStackListener mOnActivityRestartAttemptListener = new TaskStackListener() { 150 @Override 151 public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, 152 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { 153 updateForegroundDaVisibility(task.topActivity); 154 } 155 156 @Override 157 public void onTaskCreated(int taskId, ComponentName componentName) { 158 updateForegroundDaVisibility(componentName); 159 } 160 }; 161 162 /** 163 * Gets the instance of {@link CarDisplayAreaController} 164 */ getInstance()165 public static CarDisplayAreaController getInstance() { 166 return INSTANCE; 167 } 168 169 /** 170 * Initializes the controller 171 */ init(Context applicationContext, SyncTransactionQueue syncQueue, CarDisplayAreaOrganizer organizer, CarDisplayAreaTouchHandler carDisplayAreaTouchHandler)172 public void init(Context applicationContext, SyncTransactionQueue syncQueue, 173 CarDisplayAreaOrganizer organizer, 174 CarDisplayAreaTouchHandler carDisplayAreaTouchHandler) { 175 mApplicationContext = applicationContext; 176 mSyncQueue = syncQueue; 177 mOrganizer = organizer; 178 mTotalScreenHeight = applicationContext.getResources().getDimensionPixelSize( 179 R.dimen.total_screen_height); 180 mTotalScreenWidth = applicationContext.getResources().getDimensionPixelSize( 181 R.dimen.total_screen_width); 182 mControlBarDisplayHeight = applicationContext.getResources().getDimensionPixelSize( 183 R.dimen.control_bar_height); 184 mFullDisplayHeight = applicationContext.getResources().getDimensionPixelSize( 185 R.dimen.full_app_display_area_height); 186 mDefaultDisplayHeight = applicationContext.getResources().getDimensionPixelSize( 187 R.dimen.default_app_display_area_height); 188 mCarDisplayAreaTouchHandler = carDisplayAreaTouchHandler; 189 mControlBarActivityComponent = new ComponentName(applicationContext, 190 ControlBarActivity.class).flattenToShortString(); 191 mAssistantVoicePlateActivityName = ComponentName.unflattenFromString( 192 applicationContext.getResources().getString( 193 R.string.config_assistantVoicePlateActivity)); 194 mAssistUtils = new AssistUtils(applicationContext); 195 196 // Get bottom nav bar height. 197 Resources resources = applicationContext.getResources(); 198 int navBarHeight = resources.getDimensionPixelSize( 199 com.android.internal.R.dimen.navigation_bar_height); 200 if (navBarHeight > 0) { 201 mNavBarBounds.set(0, mTotalScreenHeight - navBarHeight, mTotalScreenWidth, 202 mTotalScreenHeight); 203 } 204 205 // Get left nav bar width. 206 int leftNavBarWidthResId = resources 207 .getIdentifier("car_left_system_bar_width", "dimen", "android"); 208 int leftNavBarWidth = 0; 209 if (leftNavBarWidthResId > 0) { 210 leftNavBarWidth = resources.getDimensionPixelSize(leftNavBarWidthResId); 211 mNavBarBounds.set(0, 0, leftNavBarWidth, mTotalScreenHeight); 212 } 213 214 // Get right nav bar width. 215 int rightNavBarWidthResId = resources 216 .getIdentifier("car_right_system_bar_width", "dimen", "android"); 217 int rightNavBarWidth = 0; 218 if (rightNavBarWidthResId > 0) { 219 rightNavBarWidth = resources.getDimensionPixelSize(rightNavBarWidthResId); 220 mNavBarBounds.set(mTotalScreenWidth - rightNavBarWidth, 0, mTotalScreenWidth, 221 mTotalScreenHeight); 222 } 223 224 mScreenHeightWithoutNavBar = mTotalScreenHeight - mNavBarBounds.height(); 225 mTitleBarHeight = resources.getDimensionPixelSize(R.dimen.title_bar_display_area_height); 226 mEnterExitAnimationDurationMs = applicationContext.getResources().getInteger( 227 R.integer.enter_exit_animation_foreground_display_area_duration_ms); 228 mTitleBarDragThreshold = applicationContext.getResources().getDimensionPixelSize( 229 R.dimen.title_bar_display_area_touch_drag_threshold); 230 mForegroundDisplayTop = mScreenHeightWithoutNavBar - mDefaultDisplayHeight; 231 232 mForegroundDAComponentsVisibilityMap = new HashMap<>(); 233 for (String component : mApplicationContext.getResources().getStringArray( 234 R.array.config_foregroundDAComponents)) { 235 mForegroundDAComponentsVisibilityMap.put(component, false); 236 } 237 238 String[] ignoreOpeningForegroundDACmp = mApplicationContext.getResources().getStringArray( 239 R.array.config_ignoreOpeningForegroundDA); 240 mIgnoreOpeningForegroundDAComponentsSet = new ArraySet<>(); 241 for (String component : ignoreOpeningForegroundDACmp) { 242 ComponentName componentName = ComponentName.unflattenFromString(component); 243 mIgnoreOpeningForegroundDAComponentsSet.add(componentName); 244 } 245 } 246 CarDisplayAreaController()247 private CarDisplayAreaController() { 248 } 249 shouldIgnoreOpeningForegroundDA(ActivityManager.RunningTaskInfo taskInfo)250 boolean shouldIgnoreOpeningForegroundDA(ActivityManager.RunningTaskInfo taskInfo) { 251 return taskInfo.baseIntent != null && mIgnoreOpeningForegroundDAComponentsSet.contains( 252 taskInfo.baseIntent.getComponent()); 253 } 254 255 /** 256 * Show the title bar within a targeted display area using the rootDisplayAreaId. 257 */ showTitleBar(int rootDisplayAreaId, Context context)258 public void showTitleBar(int rootDisplayAreaId, Context context) { 259 LayoutInflater inflater = LayoutInflater.from(context); 260 mTitleBarView = inflater 261 .inflate(R.layout.title_bar_display_area_view, null, true); 262 mTitleBarView.setVisibility(View.VISIBLE); 263 264 // Show the confirmation. 265 WindowManager.LayoutParams lp = getTitleBarWindowLayoutParams(); 266 getWindowManager(rootDisplayAreaId, context).addView(mTitleBarView, lp); 267 } 268 getWindowManager(int rootDisplayAreaId, Context context)269 private WindowManager getWindowManager(int rootDisplayAreaId, Context context) { 270 Bundle options = getOptionWithRootDisplayArea(rootDisplayAreaId); 271 if (mTitleBarWindowManager == null || mTitleBarWindowContext == null) { 272 // Create window context to specify the RootDisplayArea 273 mTitleBarWindowContext = context.createWindowContext( 274 TITLE_BAR_WINDOW_TYPE, options); 275 mTitleBarWindowManager = mTitleBarWindowContext.getSystemService(WindowManager.class); 276 return mTitleBarWindowManager; 277 } 278 279 // Update the window context and window manager to specify the RootDisplayArea 280 IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); 281 try { 282 wms.attachWindowContextToDisplayArea(mTitleBarWindowContext.getWindowContextToken(), 283 TITLE_BAR_WINDOW_TYPE, context.getDisplayId(), options); 284 } catch (RemoteException e) { 285 throw e.rethrowAsRuntimeException(); 286 } 287 288 return mTitleBarWindowManager; 289 } 290 291 /** 292 * Returns options that specify the {@link RootDisplayArea} to attach the confirmation window. 293 * {@code null} if the {@code rootDisplayAreaId} is {@link FEATURE_UNDEFINED}. 294 */ 295 @Nullable getOptionWithRootDisplayArea(int rootDisplayAreaId)296 private static Bundle getOptionWithRootDisplayArea(int rootDisplayAreaId) { 297 // In case we don't care which root display area the window manager is specifying. 298 if (rootDisplayAreaId == FEATURE_UNDEFINED) { 299 return null; 300 } 301 302 Bundle options = new Bundle(); 303 options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId); 304 return options; 305 } 306 getTitleBarWindowLayoutParams()307 private WindowManager.LayoutParams getTitleBarWindowLayoutParams() { 308 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 309 ViewGroup.LayoutParams.MATCH_PARENT, 310 mTitleBarHeight, 311 TITLE_BAR_WINDOW_TYPE, 312 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 313 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 314 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 315 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 316 PixelFormat.TRANSLUCENT); 317 lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.statusBars()); 318 // Trusted overlay so touches outside the touchable area are allowed to pass through 319 lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS 320 | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; 321 lp.setTitle("TitleBar"); 322 lp.gravity = Gravity.TOP; 323 lp.token = mWindowToken; 324 return lp; 325 } 326 327 /** 328 * Returns if display area hosting default application is visible to user or not. 329 */ isHostingDefaultApplicationDisplayAreaVisible()330 public boolean isHostingDefaultApplicationDisplayAreaVisible() { 331 return mIsHostingDefaultApplicationDisplayAreaVisible; 332 } 333 isDisplayAreaAnimating()334 boolean isDisplayAreaAnimating() { 335 return mOrganizer != null && mOrganizer.isDisplayAreaAnimating(); 336 } 337 338 /** Registers the DA organizer. */ register()339 public void register() { 340 mDpiDensity = mOrganizer.getDpiDensity(); 341 342 // Register DA organizer. 343 registerOrganizer(); 344 345 // Pre-calculate the foreground and background display bounds for different configs. 346 populateBounds(); 347 348 // Set the initial bounds for first and second displays. 349 WindowContainerTransaction wct = new WindowContainerTransaction(); 350 updateBounds(wct); 351 mOrganizer.applyTransaction(wct); 352 353 mCarDisplayAreaTouchHandler.registerOnClickListener((x, y) -> { 354 // Check if the click is outside the bounds of default display. If so, close the 355 // display area. 356 if (mIsHostingDefaultApplicationDisplayAreaVisible 357 && y < (mForegroundDisplayTop)) { 358 // TODO: closing logic goes here, something like: startAnimation(CONTROL_BAR); 359 } 360 }); 361 362 mCarDisplayAreaTouchHandler.registerTouchEventListener( 363 new CarDisplayAreaTouchHandler.OnDragDisplayAreaListener() { 364 365 float mCurrentPos = -1; 366 367 @Override 368 public void onStart(float x, float y) { 369 mCurrentPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight 370 - mControlBarDisplayHeight; 371 } 372 373 @Override 374 public void onMove(float x, float y) { 375 if (y <= mScreenHeightWithoutNavBar - mDefaultDisplayHeight 376 - mControlBarDisplayHeight) { 377 return; 378 } 379 animateToControlBarState((int) mCurrentPos, (int) y, 0); 380 mCurrentPos = y; 381 } 382 383 @Override 384 public void onFinish(float x, float y) { 385 if (y >= mTitleBarDragThreshold) { 386 animateToControlBarState((int) y, 387 mScreenHeightWithoutNavBar + mTitleBarHeight, 0); 388 mCarDisplayAreaTouchHandler.updateTitleBarVisibility(false); 389 // Notify the system bar button in sysui that the display area has 390 // been swiped closed 391 Intent intent = new Intent(DISPLAY_AREA_VISIBILITY_CHANGED); 392 intent.putExtra(INTENT_EXTRA_IS_DISPLAY_AREA_VISIBLE, false); 393 mApplicationContext.sendBroadcastAsUser(intent, UserHandle.ALL); 394 } else { 395 animateToDefaultState((int) y, 396 mScreenHeightWithoutNavBar - mDefaultDisplayHeight 397 - mControlBarDisplayHeight, 0); 398 } 399 } 400 }); 401 mCarDisplayAreaTouchHandler.enable(true); 402 403 ActivityTaskManager.getInstance().registerTaskStackListener( 404 mOnActivityRestartAttemptListener); 405 } 406 407 /** 408 * Return's the associated organizer. 409 */ getOrganizer()410 public CarDisplayAreaOrganizer getOrganizer() { 411 return mOrganizer; 412 } 413 updateForegroundDaVisibility(ComponentName componentName)414 private void updateForegroundDaVisibility(ComponentName componentName) { 415 if (componentName == null) { 416 return; 417 } 418 419 String packageName = componentName.getPackageName(); 420 boolean isMaps = packageName.contains(MAPS); 421 boolean ignoreOpeningForegroundDA = mIgnoreOpeningForegroundDAComponentsSet.contains( 422 componentName); 423 // Voice plate will be shown as the top most layer. Also, we don't want to change the 424 // state of the DA's when voice plate is shown. 425 boolean isVoicePlate = componentName.equals(mAssistantVoicePlateActivityName); 426 if (isVoicePlate || isMaps) { 427 return; 428 } 429 430 // Check is there is an existing session running for assist, cancel it. 431 if (mAssistUtils.isSessionRunning()) { 432 mAssistUtils.hideCurrentSession(); 433 } 434 435 if (isHostingDefaultApplicationDisplayAreaVisible()) { 436 if (mForegroundDAComponentsVisibilityMap.containsKey( 437 componentName.flattenToShortString()) 438 && mForegroundDAComponentsVisibilityMap.get( 439 componentName.flattenToShortString())) { 440 startAnimation(AppGridActivity.CAR_LAUNCHER_STATE.CONTROL_BAR); 441 } 442 } else if (!(ignoreOpeningForegroundDA || componentName.getClassName().contains( 443 LOCATION_SETTINGS_ACTIVITY) 444 || componentName.getClassName().contains(GRANT_PERMISSION_ACTIVITY))) { 445 startAnimation(AppGridActivity.CAR_LAUNCHER_STATE.DEFAULT); 446 } 447 448 if (ignoreOpeningForegroundDA || componentName.flattenToShortString().equals( 449 mControlBarActivityComponent)) { 450 return; 451 } 452 mForegroundDAComponentsVisibilityMap.replaceAll( 453 (n, v) -> componentName.flattenToShortString().equals(n)); 454 } 455 showVoicePlateDisplayArea()456 void showVoicePlateDisplayArea() { 457 SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); 458 tx.show(mVoicePlateDisplay.getLeash()); 459 } 460 resetVoicePlateDisplayArea()461 void resetVoicePlateDisplayArea() { 462 SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); 463 tx.hide(mVoicePlateDisplay.getLeash()); 464 } 465 466 /** Registers DA organizer. */ registerOrganizer()467 private void registerOrganizer() { 468 List<DisplayAreaAppearedInfo> foregroundDisplayAreaInfos = 469 mOrganizer.registerOrganizer(FOREGROUND_DISPLAY_AREA_ROOT); 470 if (foregroundDisplayAreaInfos.size() != 1) { 471 throw new IllegalStateException("Can't find display to launch default applications"); 472 } 473 474 List<DisplayAreaAppearedInfo> titleBarDisplayAreaInfo = 475 mOrganizer.registerOrganizer(FEATURE_TITLE_BAR); 476 if (titleBarDisplayAreaInfo.size() != 1) { 477 throw new IllegalStateException("Can't find display to launch title bar"); 478 } 479 480 List<DisplayAreaAppearedInfo> voicePlateDisplayAreaInfo = 481 mOrganizer.registerOrganizer(FEATURE_VOICE_PLATE); 482 if (voicePlateDisplayAreaInfo.size() != 1) { 483 throw new IllegalStateException("Can't find display to launch voice plate"); 484 } 485 486 List<DisplayAreaAppearedInfo> backgroundDisplayAreaInfos = 487 mOrganizer.registerOrganizer(BACKGROUND_TASK_CONTAINER); 488 if (backgroundDisplayAreaInfos.size() != 1) { 489 throw new IllegalStateException("Can't find display to launch activity in background"); 490 } 491 492 List<DisplayAreaAppearedInfo> controlBarDisplayAreaInfos = 493 mOrganizer.registerOrganizer(CONTROL_BAR_DISPLAY_AREA); 494 if (controlBarDisplayAreaInfos.size() != 1) { 495 throw new IllegalStateException("Can't find display to launch audio control"); 496 } 497 498 // Get the IME display area attached to the root hierarchy. 499 List<DisplayAreaAppearedInfo> imeDisplayAreaInfos = 500 mOrganizer.registerOrganizer(FEATURE_IME_PLACEHOLDER); 501 for (DisplayAreaAppearedInfo info : imeDisplayAreaInfos) { 502 if (info.getDisplayAreaInfo().rootDisplayAreaId == FEATURE_ROOT) { 503 mImeContainerDisplayArea = info; 504 } 505 } 506 // As we have only 1 display defined for each display area feature get the 0th index. 507 mForegroundApplicationsDisplay = foregroundDisplayAreaInfos.get(0); 508 mTitleBarDisplay = titleBarDisplayAreaInfo.get(0); 509 mVoicePlateDisplay = voicePlateDisplayAreaInfo.get(0); 510 mBackgroundApplicationDisplay = backgroundDisplayAreaInfos.get(0); 511 mControlBarDisplay = controlBarDisplayAreaInfos.get(0); 512 513 SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); 514 // TODO(b/188102153): replace to set mForegroundApplicationsDisplay to top. 515 tx.setLayer(mBackgroundApplicationDisplay.getLeash(), BACKGROUND_LAYER_INDEX); 516 tx.setLayer(mForegroundApplicationsDisplay.getLeash(), FOREGROUND_LAYER_INDEX); 517 tx.setLayer(mTitleBarDisplay.getLeash(), FOREGROUND_LAYER_INDEX); 518 tx.setLayer(mVoicePlateDisplay.getLeash(), VOICE_PLATE_LAYER_SHOWN_INDEX); 519 tx.setLayer(mControlBarDisplay.getLeash(), CONTROL_BAR_LAYER_INDEX); 520 521 tx.hide(mVoicePlateDisplay.getLeash()); 522 tx.hide(mForegroundApplicationsDisplay.getLeash()); 523 tx.apply(); 524 } 525 526 /** Un-Registers DA organizer. */ unregister()527 public void unregister() { 528 mOrganizer.resetWindowsOffset(); 529 mOrganizer.unregisterOrganizer(); 530 mForegroundApplicationsDisplay = null; 531 mTitleBarDisplay = null; 532 mBackgroundApplicationDisplay = null; 533 mControlBarDisplay = null; 534 mVoicePlateDisplay = null; 535 mImeContainerDisplayArea = null; 536 mCarDisplayAreaTouchHandler.enable(false); 537 ActivityTaskManager.getInstance() 538 .unregisterTaskStackListener(mOnActivityRestartAttemptListener); 539 mTitleBarView.setVisibility(View.GONE); 540 } 541 542 /** 543 * This method should be called after the registration of DA's are done. The method expects a 544 * target state as an argument, according to which the animations will take place. For example, 545 * if the target state is {@link AppGridActivity.CAR_LAUNCHER_STATE#DEFAULT} then the foreground 546 * DA hosting default applications will animate to the default set height. 547 */ startAnimation(AppGridActivity.CAR_LAUNCHER_STATE toState)548 public void startAnimation(AppGridActivity.CAR_LAUNCHER_STATE toState) { 549 // TODO: currently the animations are only bottom/up. Make it more generic animations here. 550 int fromPos = 0; 551 int toPos = 0; 552 553 Intent intent = new Intent(DISPLAY_AREA_VISIBILITY_CHANGED); 554 switch (toState) { 555 case CONTROL_BAR: 556 // Foreground DA closes. 557 fromPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight 558 - mControlBarDisplayHeight; 559 toPos = mScreenHeightWithoutNavBar + mTitleBarHeight; 560 animateToControlBarState(fromPos, toPos, mEnterExitAnimationDurationMs); 561 mCarDisplayAreaTouchHandler.updateTitleBarVisibility(false); 562 intent.putExtra(INTENT_EXTRA_IS_DISPLAY_AREA_VISIBLE, false); 563 break; 564 case FULL: 565 // TODO: Implement this. 566 break; 567 default: 568 // Foreground DA opens to default height. 569 // update the bounds to expand the foreground display area before starting 570 // animations. 571 fromPos = mScreenHeightWithoutNavBar + mTitleBarHeight; 572 toPos = mScreenHeightWithoutNavBar - mDefaultDisplayHeight 573 - mControlBarDisplayHeight; 574 intent.putExtra(INTENT_EXTRA_IS_DISPLAY_AREA_VISIBLE, true); 575 animateToDefaultState(fromPos, toPos, mEnterExitAnimationDurationMs); 576 } 577 mApplicationContext.sendBroadcastAsUser(intent, UserHandle.ALL); 578 } 579 animateToControlBarState(int fromPos, int toPos, int durationMs)580 private void animateToControlBarState(int fromPos, int toPos, int durationMs) { 581 mBackgroundApplicationDisplayBounds.bottom = 582 mScreenHeightWithoutNavBar - mControlBarDisplayHeight; 583 animate(fromPos, toPos, CONTROL_BAR, durationMs); 584 mIsHostingDefaultApplicationDisplayAreaVisible = false; 585 } 586 animateToDefaultState(int fromPos, int toPos, int durationMs)587 private void animateToDefaultState(int fromPos, int toPos, int durationMs) { 588 if (!mIsForegroundDaVisible) { 589 SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); 590 tx.show(mForegroundApplicationsDisplay.getLeash()); 591 tx.apply(true); 592 mIsForegroundDaVisible = true; 593 } 594 mBackgroundApplicationDisplayBounds.bottom = toPos - mTitleBarHeight; 595 animate(fromPos, toPos, DEFAULT, durationMs); 596 mIsHostingDefaultApplicationDisplayAreaVisible = true; 597 if (mCarDisplayAreaTouchHandler != null) { 598 mCarDisplayAreaTouchHandler.updateTitleBarVisibility(true); 599 } 600 } 601 animate(int fromPos, int toPos, AppGridActivity.CAR_LAUNCHER_STATE toState, int durationMs)602 private void animate(int fromPos, int toPos, AppGridActivity.CAR_LAUNCHER_STATE toState, 603 int durationMs) { 604 if (mOrganizer != null) { 605 mOrganizer.scheduleOffset(fromPos, toPos, mBackgroundApplicationDisplayBounds, 606 mBackgroundApplicationDisplay, mForegroundApplicationsDisplay, 607 mTitleBarDisplay, 608 mControlBarDisplay, toState, durationMs); 609 } 610 } 611 612 /** Pre-calculates the default and background display bounds for different configs. */ populateBounds()613 private void populateBounds() { 614 int controlBarTop = mScreenHeightWithoutNavBar - mControlBarDisplayHeight; 615 int foregroundTop = 616 mScreenHeightWithoutNavBar - mDefaultDisplayHeight - mControlBarDisplayHeight; 617 618 // Bottom nav bar. Bottom nav bar height will be 0 if the nav bar is present on the sides. 619 Rect backgroundBounds = new Rect(0, 0, mTotalScreenWidth, controlBarTop); 620 Rect controlBarBounds = new Rect(0, controlBarTop, mTotalScreenWidth, 621 mScreenHeightWithoutNavBar); 622 Rect foregroundBounds = new Rect(0, 623 foregroundTop, mTotalScreenWidth, 624 mScreenHeightWithoutNavBar - mControlBarDisplayHeight); 625 Rect voicePlateBounds = new Rect(0, 0, mTotalScreenWidth, 626 mScreenHeightWithoutNavBar - mControlBarDisplayHeight); 627 Rect titleBarBounds = new Rect(0, 628 foregroundTop - mTitleBarHeight, mTotalScreenWidth, foregroundTop); 629 630 // Adjust the bounds based on the nav bar. 631 // TODO: account for the case where nav bar is at the top. 632 633 // Populate the bounds depending on where the nav bar is. 634 if (mNavBarBounds.left == 0 && mNavBarBounds.top == 0) { 635 // Left nav bar. 636 backgroundBounds.left = mNavBarBounds.right; 637 controlBarBounds.left = mNavBarBounds.right; 638 foregroundBounds.left = mNavBarBounds.right; 639 titleBarBounds.left = mNavBarBounds.right; 640 } else if (mNavBarBounds.top == 0) { 641 // Right nav bar. 642 backgroundBounds.right = mNavBarBounds.left; 643 controlBarBounds.right = mNavBarBounds.left; 644 foregroundBounds.right = mNavBarBounds.left; 645 titleBarBounds.right = mNavBarBounds.left; 646 } 647 648 mBackgroundApplicationDisplayBounds.set(backgroundBounds); 649 mControlBarDisplayBounds.set(controlBarBounds); 650 mForegroundApplicationDisplayBounds.set(foregroundBounds); 651 mTitleBarDisplayBounds.set(titleBarBounds); 652 mVoicePlateDisplayBounds.set(voicePlateBounds); 653 mCarDisplayAreaTouchHandler.setTitleBarBounds(titleBarBounds); 654 } 655 656 /** Updates the default and background display bounds for the given config. */ updateBounds(WindowContainerTransaction wct)657 private void updateBounds(WindowContainerTransaction wct) { 658 Rect foregroundApplicationDisplayBound = mForegroundApplicationDisplayBounds; 659 Rect titleBarDisplayBounds = mTitleBarDisplayBounds; 660 Rect voicePlateDisplayBounds = mVoicePlateDisplayBounds; 661 Rect backgroundApplicationDisplayBound = mBackgroundApplicationDisplayBounds; 662 Rect controlBarDisplayBound = mControlBarDisplayBounds; 663 664 WindowContainerToken foregroundDisplayToken = 665 mForegroundApplicationsDisplay.getDisplayAreaInfo().token; 666 WindowContainerToken imeRootDisplayToken = 667 mImeContainerDisplayArea.getDisplayAreaInfo().token; 668 WindowContainerToken titleBarDisplayToken = 669 mTitleBarDisplay.getDisplayAreaInfo().token; 670 WindowContainerToken voicePlateDisplayToken = 671 mVoicePlateDisplay.getDisplayAreaInfo().token; 672 WindowContainerToken backgroundDisplayToken = 673 mBackgroundApplicationDisplay.getDisplayAreaInfo().token; 674 WindowContainerToken controlBarDisplayToken = 675 mControlBarDisplay.getDisplayAreaInfo().token; 676 677 // Default TDA 678 int foregroundDisplayWidthDp = 679 foregroundApplicationDisplayBound.width() * DisplayMetrics.DENSITY_DEFAULT 680 / mDpiDensity; 681 int foregroundDisplayHeightDp = 682 foregroundApplicationDisplayBound.height() * DisplayMetrics.DENSITY_DEFAULT 683 / mDpiDensity; 684 wct.setBounds(foregroundDisplayToken, foregroundApplicationDisplayBound); 685 wct.setScreenSizeDp(foregroundDisplayToken, foregroundDisplayWidthDp, 686 foregroundDisplayHeightDp); 687 wct.setSmallestScreenWidthDp(foregroundDisplayToken, foregroundDisplayWidthDp); 688 689 // Title bar 690 int titleBarDisplayWidthDp = 691 titleBarDisplayBounds.width() * DisplayMetrics.DENSITY_DEFAULT 692 / mDpiDensity; 693 int titleBarDisplayHeightDp = 694 titleBarDisplayBounds.height() * DisplayMetrics.DENSITY_DEFAULT 695 / mDpiDensity; 696 wct.setBounds(titleBarDisplayToken, titleBarDisplayBounds); 697 wct.setScreenSizeDp(titleBarDisplayToken, titleBarDisplayWidthDp, 698 titleBarDisplayHeightDp); 699 wct.setSmallestScreenWidthDp(titleBarDisplayToken, titleBarDisplayWidthDp); 700 701 // voice plate 702 int voicePlateDisplayWidthDp = 703 voicePlateDisplayBounds.width() * DisplayMetrics.DENSITY_DEFAULT 704 / mDpiDensity; 705 int voicePlateDisplayHeightDp = 706 voicePlateDisplayBounds.height() * DisplayMetrics.DENSITY_DEFAULT 707 / mDpiDensity; 708 wct.setBounds(voicePlateDisplayToken, voicePlateDisplayBounds); 709 wct.setScreenSizeDp(voicePlateDisplayToken, voicePlateDisplayWidthDp, 710 voicePlateDisplayHeightDp); 711 wct.setSmallestScreenWidthDp(voicePlateDisplayToken, voicePlateDisplayWidthDp); 712 713 // background TDA 714 int backgroundDisplayWidthDp = 715 backgroundApplicationDisplayBound.width() * DisplayMetrics.DENSITY_DEFAULT 716 / mDpiDensity; 717 int backgroundDisplayHeightDp = 718 backgroundApplicationDisplayBound.height() * DisplayMetrics.DENSITY_DEFAULT 719 / mDpiDensity; 720 wct.setBounds(backgroundDisplayToken, backgroundApplicationDisplayBound); 721 wct.setScreenSizeDp(backgroundDisplayToken, backgroundDisplayWidthDp, 722 backgroundDisplayHeightDp); 723 wct.setSmallestScreenWidthDp(backgroundDisplayToken, backgroundDisplayWidthDp); 724 725 // Change the bounds of the IME attached to the root display to be same as the background DA 726 wct.setBounds(imeRootDisplayToken, backgroundApplicationDisplayBound); 727 wct.setScreenSizeDp(imeRootDisplayToken, backgroundDisplayWidthDp, 728 backgroundDisplayHeightDp); 729 wct.setSmallestScreenWidthDp(imeRootDisplayToken, backgroundDisplayWidthDp); 730 731 // control bar 732 int controlBarDisplayWidthDp = 733 controlBarDisplayBound.width() * DisplayMetrics.DENSITY_DEFAULT 734 / mDpiDensity; 735 int controlBarDisplayHeightDp = 736 controlBarDisplayBound.height() * DisplayMetrics.DENSITY_DEFAULT 737 / mDpiDensity; 738 wct.setBounds(controlBarDisplayToken, controlBarDisplayBound); 739 wct.setScreenSizeDp(controlBarDisplayToken, controlBarDisplayWidthDp, 740 controlBarDisplayHeightDp); 741 wct.setSmallestScreenWidthDp(controlBarDisplayToken, controlBarDisplayWidthDp); 742 743 mSyncQueue.runInSync(t -> { 744 t.setPosition(mForegroundApplicationsDisplay.getLeash(), 745 foregroundApplicationDisplayBound.left, 746 foregroundApplicationDisplayBound.top); 747 t.setPosition(mVoicePlateDisplay.getLeash(), 748 voicePlateDisplayBounds.left, 749 voicePlateDisplayBounds.top); 750 t.setPosition(mTitleBarDisplay.getLeash(), 751 titleBarDisplayBounds.left, -mTitleBarHeight); 752 t.setPosition(mBackgroundApplicationDisplay.getLeash(), 753 backgroundApplicationDisplayBound.left, 754 backgroundApplicationDisplayBound.top); 755 t.setPosition(mImeContainerDisplayArea.getLeash(), 756 backgroundApplicationDisplayBound.left, 757 backgroundApplicationDisplayBound.top); 758 t.setPosition(mControlBarDisplay.getLeash(), 759 controlBarDisplayBound.left, 760 controlBarDisplayBound.top); 761 }); 762 } 763 764 /** 765 * Update the bounds of foreground DA to cover full screen. 766 */ makeForegroundDAFullscreen()767 public void makeForegroundDAFullscreen() { 768 WindowContainerTransaction wct = new WindowContainerTransaction(); 769 Rect foregroundApplicationDisplayBounds = new Rect(0, 0, mTotalScreenWidth, 770 mTotalScreenHeight); 771 WindowContainerToken foregroundDisplayToken = 772 mForegroundApplicationsDisplay.getDisplayAreaInfo().token; 773 774 int foregroundDisplayWidthDp = 775 foregroundApplicationDisplayBounds.width() * DisplayMetrics.DENSITY_DEFAULT 776 / mDpiDensity; 777 int foregroundDisplayHeightDp = 778 foregroundApplicationDisplayBounds.height() * DisplayMetrics.DENSITY_DEFAULT 779 / mDpiDensity; 780 wct.setBounds(foregroundDisplayToken, foregroundApplicationDisplayBounds); 781 wct.setScreenSizeDp(foregroundDisplayToken, foregroundDisplayWidthDp, 782 foregroundDisplayHeightDp); 783 wct.setSmallestScreenWidthDp(foregroundDisplayToken, 784 Math.min(foregroundDisplayWidthDp, foregroundDisplayHeightDp)); 785 786 mSyncQueue.runInSync(t -> { 787 t.setPosition(mBackgroundApplicationDisplay.getLeash(), 0, 0); 788 }); 789 790 mOrganizer.applyTransaction(wct); 791 } 792 } 793 794