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 package com.android.launcher3.taskbar; 17 18 import static android.content.Context.RECEIVER_NOT_EXPORTED; 19 import static android.view.Display.DEFAULT_DISPLAY; 20 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 21 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 22 23 import static com.android.launcher3.BaseActivity.EVENT_DESTROYED; 24 import static com.android.launcher3.Flags.enableUnfoldStateAnimation; 25 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; 26 import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate; 27 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; 28 import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE; 29 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; 30 import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING; 31 import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG; 32 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 33 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; 34 import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR; 35 import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR; 36 37 import android.annotation.SuppressLint; 38 import android.app.PendingIntent; 39 import android.content.ComponentCallbacks; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.content.pm.ActivityInfo; 44 import android.content.res.Configuration; 45 import android.hardware.display.DisplayManager; 46 import android.net.Uri; 47 import android.os.Handler; 48 import android.os.Trace; 49 import android.provider.Settings; 50 import android.util.Log; 51 import android.view.Display; 52 import android.view.MotionEvent; 53 import android.view.WindowManager; 54 import android.widget.FrameLayout; 55 56 import androidx.annotation.NonNull; 57 import androidx.annotation.Nullable; 58 import androidx.annotation.VisibleForTesting; 59 60 import com.android.launcher3.DeviceProfile; 61 import com.android.launcher3.Launcher; 62 import com.android.launcher3.LauncherAppState; 63 import com.android.launcher3.anim.AnimatorPlaybackController; 64 import com.android.launcher3.statemanager.StatefulActivity; 65 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks; 66 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider; 67 import com.android.launcher3.uioverrides.QuickstepLauncher; 68 import com.android.launcher3.util.DisplayController; 69 import com.android.launcher3.util.SettingsCache; 70 import com.android.launcher3.util.SimpleBroadcastReceiver; 71 import com.android.quickstep.AllAppsActionManager; 72 import com.android.quickstep.RecentsActivity; 73 import com.android.quickstep.SystemUiProxy; 74 import com.android.quickstep.util.AssistUtils; 75 import com.android.systemui.shared.system.QuickStepContract; 76 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; 77 import com.android.systemui.unfold.UnfoldTransitionProgressProvider; 78 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; 79 80 import java.io.PrintWriter; 81 import java.util.StringJoiner; 82 83 /** 84 * Class to manage taskbar lifecycle 85 */ 86 public class TaskbarManager { 87 private static final String TAG = "TaskbarManager"; 88 private static final boolean DEBUG = false; 89 90 /** 91 * All the configurations which do not initiate taskbar recreation. 92 * This includes all the configurations defined in Launcher's manifest entry and 93 * ActivityController#filterConfigChanges 94 */ 95 private static final int SKIP_RECREATE_CONFIG_CHANGES = ActivityInfo.CONFIG_WINDOW_CONFIGURATION 96 | ActivityInfo.CONFIG_KEYBOARD 97 | ActivityInfo.CONFIG_KEYBOARD_HIDDEN 98 | ActivityInfo.CONFIG_MCC 99 | ActivityInfo.CONFIG_MNC 100 | ActivityInfo.CONFIG_NAVIGATION 101 | ActivityInfo.CONFIG_ORIENTATION 102 | ActivityInfo.CONFIG_SCREEN_SIZE 103 | ActivityInfo.CONFIG_SCREEN_LAYOUT 104 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; 105 106 private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor( 107 Settings.Secure.USER_SETUP_COMPLETE); 108 109 private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor( 110 Settings.Secure.NAV_BAR_KIDS_MODE); 111 112 private final Context mContext; 113 private final @Nullable Context mNavigationBarPanelContext; 114 private WindowManager mWindowManager; 115 private FrameLayout mTaskbarRootLayout; 116 private boolean mAddedWindow; 117 private final TaskbarNavButtonController mNavButtonController; 118 private final ComponentCallbacks mComponentCallbacks; 119 120 private final SimpleBroadcastReceiver mShutdownReceiver = 121 new SimpleBroadcastReceiver(i -> destroyExistingTaskbar()); 122 123 // The source for this provider is set when Launcher is available 124 // We use 'non-destroyable' version here so the original provider won't be destroyed 125 // as it is tied to the activity lifecycle, not the taskbar lifecycle. 126 // It's destruction/creation will be managed by the activity. 127 private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider = 128 new NonDestroyableScopedUnfoldTransitionProgressProvider(); 129 130 private TaskbarActivityContext mTaskbarActivityContext; 131 private StatefulActivity mActivity; 132 /** 133 * Cache a copy here so we can initialize state whenever taskbar is recreated, since 134 * this class does not get re-initialized w/ new taskbars. 135 */ 136 private final TaskbarSharedState mSharedState = new TaskbarSharedState(); 137 138 /** 139 * We use WindowManager's ComponentCallbacks() for internal UI changes (similar to an Activity) 140 * which comes via a different channel 141 */ 142 private final RecreationListener mRecreationListener = new RecreationListener(); 143 144 private class RecreationListener implements DisplayController.DisplayInfoChangeListener { 145 @Override onDisplayInfoChanged(Context context, DisplayController.Info info, int flags)146 public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) { 147 if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE | CHANGE_DESKTOP_MODE 148 | CHANGE_TASKBAR_PINNING)) != 0) { 149 recreateTaskbar(); 150 } 151 } 152 } 153 private final SettingsCache.OnChangeListener mOnSettingsChangeListener = c -> recreateTaskbar(); 154 155 private boolean mUserUnlocked = false; 156 157 private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver = 158 new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast); 159 160 private final AllAppsActionManager mAllAppsActionManager; 161 162 private final Runnable mActivityOnDestroyCallback = new Runnable() { 163 @Override 164 public void run() { 165 if (mActivity != null) { 166 mActivity.removeOnDeviceProfileChangeListener( 167 mDebugActivityDeviceProfileChanged); 168 Log.d(TASKBAR_NOT_DESTROYED_TAG, 169 "unregistering activity lifecycle callbacks from " 170 + "onActivityDestroyed."); 171 mActivity.removeEventCallback(EVENT_DESTROYED, this); 172 } 173 mActivity = null; 174 debugWhyTaskbarNotDestroyed("clearActivity"); 175 if (mTaskbarActivityContext != null) { 176 mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT); 177 } 178 mUnfoldProgressProvider.setSourceProvider(null); 179 } 180 }; 181 182 UnfoldTransitionProgressProvider.TransitionProgressListener mUnfoldTransitionProgressListener = 183 new UnfoldTransitionProgressProvider.TransitionProgressListener() { 184 @Override 185 public void onTransitionStarted() { 186 Log.d(TASKBAR_NOT_DESTROYED_TAG, 187 "fold/unfold transition started getting called."); 188 } 189 190 @Override 191 public void onTransitionProgress(float progress) { 192 Log.d(TASKBAR_NOT_DESTROYED_TAG, 193 "fold/unfold transition progress : " + progress); 194 } 195 196 @Override 197 public void onTransitionFinishing() { 198 Log.d(TASKBAR_NOT_DESTROYED_TAG, 199 "fold/unfold transition finishing getting called."); 200 201 } 202 203 @Override 204 public void onTransitionFinished() { 205 Log.d(TASKBAR_NOT_DESTROYED_TAG, 206 "fold/unfold transition finished getting called."); 207 208 } 209 }; 210 211 @SuppressLint("WrongConstant") TaskbarManager( Context context, AllAppsActionManager allAppsActionManager, TaskbarNavButtonCallbacks navCallbacks)212 public TaskbarManager( 213 Context context, 214 AllAppsActionManager allAppsActionManager, 215 TaskbarNavButtonCallbacks navCallbacks) { 216 217 Display display = 218 context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); 219 mContext = context.createWindowContext(display, 220 ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL, 221 null); 222 mAllAppsActionManager = allAppsActionManager; 223 mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION 224 ? context.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null) 225 : null; 226 if (enableTaskbarNoRecreate()) { 227 mWindowManager = mContext.getSystemService(WindowManager.class); 228 mTaskbarRootLayout = new FrameLayout(mContext) { 229 @Override 230 public boolean dispatchTouchEvent(MotionEvent ev) { 231 // The motion events can be outside the view bounds of task bar, and hence 232 // manually dispatching them to the drag layer here. 233 if (mTaskbarActivityContext != null 234 && mTaskbarActivityContext.getDragLayer().isAttachedToWindow()) { 235 return mTaskbarActivityContext.getDragLayer().dispatchTouchEvent(ev); 236 } 237 return super.dispatchTouchEvent(ev); 238 } 239 }; 240 } 241 mNavButtonController = new TaskbarNavButtonController( 242 context, 243 navCallbacks, 244 SystemUiProxy.INSTANCE.get(mContext), 245 new Handler(), 246 AssistUtils.newInstance(mContext)); 247 mComponentCallbacks = new ComponentCallbacks() { 248 private Configuration mOldConfig = mContext.getResources().getConfiguration(); 249 250 @Override 251 public void onConfigurationChanged(Configuration newConfig) { 252 Trace.instantForTrack(Trace.TRACE_TAG_APP, "TaskbarManager", 253 "onConfigurationChanged: " + newConfig); 254 debugWhyTaskbarNotDestroyed( 255 "TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig); 256 DeviceProfile dp = mUserUnlocked 257 ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) 258 : null; 259 int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES; 260 261 if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) { 262 // Only recreate for theme changes, not other UI mode changes such as docking. 263 int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); 264 int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); 265 if (oldUiNightMode == newUiNightMode) { 266 configDiff &= ~ActivityInfo.CONFIG_UI_MODE; 267 } 268 } 269 270 debugWhyTaskbarNotDestroyed("ComponentCallbacks#onConfigurationChanged() " 271 + "configDiff=" + Configuration.configurationDiffToString(configDiff)); 272 if (configDiff != 0 || mTaskbarActivityContext == null) { 273 recreateTaskbar(); 274 } else { 275 // Config change might be handled without re-creating the taskbar 276 if (dp != null && !isTaskbarEnabled(dp)) { 277 destroyExistingTaskbar(); 278 } else { 279 if (dp != null && isTaskbarEnabled(dp)) { 280 if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) { 281 // Re-initialize for screen size change? Should this be done 282 // by looking at screen-size change flag in configDiff in the 283 // block above? 284 recreateTaskbar(); 285 } else { 286 mTaskbarActivityContext.updateDeviceProfile(dp); 287 } 288 } 289 mTaskbarActivityContext.onConfigurationChanged(configDiff); 290 } 291 } 292 mOldConfig = new Configuration(newConfig); 293 // reset taskbar was pinned value, so we don't automatically unstash taskbar upon 294 // user unfolding the device. 295 mSharedState.setTaskbarWasPinned(false); 296 } 297 298 @Override 299 public void onLowMemory() { } 300 }; 301 SettingsCache.INSTANCE.get(mContext) 302 .register(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener); 303 SettingsCache.INSTANCE.get(mContext) 304 .register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener); 305 Log.d(TASKBAR_NOT_DESTROYED_TAG, "registering component callbacks from constructor."); 306 mContext.registerComponentCallbacks(mComponentCallbacks); 307 mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN); 308 UI_HELPER_EXECUTOR.execute(() -> { 309 mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast( 310 mContext, 311 SYSTEM_ACTION_ID_TASKBAR, 312 new Intent(ACTION_SHOW_TASKBAR).setPackage(mContext.getPackageName()), 313 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 314 mContext.registerReceiver( 315 mTaskbarBroadcastReceiver, 316 new IntentFilter(ACTION_SHOW_TASKBAR), 317 RECEIVER_NOT_EXPORTED); 318 }); 319 320 debugWhyTaskbarNotDestroyed("TaskbarManager created"); 321 recreateTaskbar(); 322 } 323 destroyExistingTaskbar()324 private void destroyExistingTaskbar() { 325 debugWhyTaskbarNotDestroyed("destroyExistingTaskbar: " + mTaskbarActivityContext); 326 if (mTaskbarActivityContext != null) { 327 mTaskbarActivityContext.onDestroy(); 328 if (!ENABLE_TASKBAR_NAVBAR_UNIFICATION || enableTaskbarNoRecreate()) { 329 mTaskbarActivityContext = null; 330 } 331 } 332 DeviceProfile dp = mUserUnlocked ? 333 LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; 334 if (dp == null || !isTaskbarEnabled(dp)) { 335 removeTaskbarRootViewFromWindow(); 336 } 337 } 338 339 /** 340 * Show Taskbar upon receiving broadcast 341 */ showTaskbarFromBroadcast(Intent intent)342 private void showTaskbarFromBroadcast(Intent intent) { 343 if (ACTION_SHOW_TASKBAR.equals(intent.getAction()) && mTaskbarActivityContext != null) { 344 mTaskbarActivityContext.showTaskbarFromBroadcast(); 345 } 346 } 347 348 /** 349 * Toggles All Apps for Taskbar or Launcher depending on the current state. 350 */ toggleAllApps()351 public void toggleAllApps() { 352 if (mTaskbarActivityContext == null || mTaskbarActivityContext.canToggleHomeAllApps()) { 353 // Home All Apps should be toggled from this class, because the controllers are not 354 // initialized when Taskbar is disabled (i.e. TaskbarActivityContext is null). 355 if (mActivity instanceof Launcher l) l.toggleAllAppsSearch(); 356 } else { 357 mTaskbarActivityContext.toggleAllAppsSearch(); 358 } 359 } 360 361 /** 362 * Displays a frame of the first Launcher reveal animation. 363 * 364 * This should be used to run a first Launcher reveal animation whose progress matches a swipe 365 * progress. 366 */ createLauncherStartFromSuwAnim(int duration)367 public AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) { 368 return mTaskbarActivityContext == null 369 ? null : mTaskbarActivityContext.createLauncherStartFromSuwAnim(duration); 370 } 371 372 /** 373 * Called when the user is unlocked 374 */ onUserUnlocked()375 public void onUserUnlocked() { 376 mUserUnlocked = true; 377 DisplayController.INSTANCE.get(mContext).addChangeListener(mRecreationListener); 378 recreateTaskbar(); 379 addTaskbarRootViewToWindow(); 380 } 381 382 /** 383 * Sets a {@link StatefulActivity} to act as taskbar callback 384 */ setActivity(@onNull StatefulActivity activity)385 public void setActivity(@NonNull StatefulActivity activity) { 386 if (mActivity == activity) { 387 return; 388 } 389 removeActivityCallbacksAndListeners(); 390 mActivity = activity; 391 debugWhyTaskbarNotDestroyed("Set mActivity=" + mActivity); 392 mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); 393 Log.d(TASKBAR_NOT_DESTROYED_TAG, 394 "registering activity lifecycle callbacks from setActivity()."); 395 mActivity.addEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback); 396 UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = 397 getUnfoldTransitionProgressProviderForActivity(activity); 398 if (unfoldTransitionProgressProvider != null) { 399 unfoldTransitionProgressProvider.addCallback(mUnfoldTransitionProgressListener); 400 } 401 mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider); 402 403 if (mTaskbarActivityContext != null) { 404 mTaskbarActivityContext.setUIController( 405 createTaskbarUIControllerForActivity(mActivity)); 406 } 407 } 408 409 /** 410 * Returns an {@link UnfoldTransitionProgressProvider} to use while the given StatefulActivity 411 * is active. 412 */ getUnfoldTransitionProgressProviderForActivity( StatefulActivity activity)413 private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity( 414 StatefulActivity activity) { 415 if (!enableUnfoldStateAnimation()) { 416 if (activity instanceof QuickstepLauncher ql) { 417 return ql.getUnfoldTransitionProgressProvider(); 418 } 419 } else { 420 return SystemUiProxy.INSTANCE.get(mContext).getUnfoldTransitionProvider(); 421 } 422 return null; 423 } 424 425 /** 426 * Creates a {@link TaskbarUIController} to use while the given StatefulActivity is active. 427 */ createTaskbarUIControllerForActivity(StatefulActivity activity)428 private TaskbarUIController createTaskbarUIControllerForActivity(StatefulActivity activity) { 429 if (activity instanceof QuickstepLauncher) { 430 return new LauncherTaskbarUIController((QuickstepLauncher) activity); 431 } 432 if (activity instanceof RecentsActivity) { 433 return new FallbackTaskbarUIController((RecentsActivity) activity); 434 } 435 return TaskbarUIController.DEFAULT; 436 } 437 438 /** 439 * This method is called multiple times (ex. initial init, then when user unlocks) in which case 440 * we fully want to destroy an existing taskbar and create a new one. 441 * In other case (folding/unfolding) we don't need to remove and add window. 442 */ 443 @VisibleForTesting recreateTaskbar()444 public synchronized void recreateTaskbar() { 445 Trace.beginSection("recreateTaskbar"); 446 try { 447 DeviceProfile dp = mUserUnlocked ? 448 LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; 449 450 // All Apps action is unrelated to navbar unification, so we only need to check DP. 451 final boolean isLargeScreenTaskbar = dp != null && dp.isTaskbarPresent; 452 mAllAppsActionManager.setTaskbarPresent(isLargeScreenTaskbar); 453 454 destroyExistingTaskbar(); 455 456 boolean isTaskbarEnabled = dp != null && isTaskbarEnabled(dp); 457 debugWhyTaskbarNotDestroyed("recreateTaskbar: isTaskbarEnabled=" + isTaskbarEnabled 458 + " [dp != null (i.e. mUserUnlocked)]=" + (dp != null) 459 + " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION 460 + " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent)); 461 if (!isTaskbarEnabled) { 462 SystemUiProxy.INSTANCE.get(mContext) 463 .notifyTaskbarStatus(/* visible */ false, /* stashed */ false); 464 return; 465 } 466 467 if (enableTaskbarNoRecreate() || mTaskbarActivityContext == null) { 468 mTaskbarActivityContext = new TaskbarActivityContext(mContext, 469 mNavigationBarPanelContext, dp, mNavButtonController, 470 mUnfoldProgressProvider); 471 } else { 472 mTaskbarActivityContext.updateDeviceProfile(dp); 473 } 474 mSharedState.startTaskbarVariantIsTransient = 475 DisplayController.isTransientTaskbar(mTaskbarActivityContext); 476 mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar; 477 mTaskbarActivityContext.init(mSharedState); 478 479 if (mActivity != null) { 480 mTaskbarActivityContext.setUIController( 481 createTaskbarUIControllerForActivity(mActivity)); 482 } 483 484 if (enableTaskbarNoRecreate()) { 485 addTaskbarRootViewToWindow(); 486 mTaskbarRootLayout.removeAllViews(); 487 mTaskbarRootLayout.addView(mTaskbarActivityContext.getDragLayer()); 488 mTaskbarActivityContext.notifyUpdateLayoutParams(); 489 } 490 } finally { 491 Trace.endSection(); 492 } 493 } 494 onSystemUiFlagsChanged(@ystemUiStateFlags long systemUiStateFlags)495 public void onSystemUiFlagsChanged(@SystemUiStateFlags long systemUiStateFlags) { 496 if (DEBUG) { 497 Log.d(TAG, "SysUI flags changed: " + formatFlagChange(systemUiStateFlags, 498 mSharedState.sysuiStateFlags, QuickStepContract::getSystemUiStateString)); 499 } 500 mSharedState.sysuiStateFlags = systemUiStateFlags; 501 if (mTaskbarActivityContext != null) { 502 mTaskbarActivityContext.updateSysuiStateFlags(systemUiStateFlags, false /* fromInit */); 503 } 504 } 505 onLongPressHomeEnabled(boolean assistantLongPressEnabled)506 public void onLongPressHomeEnabled(boolean assistantLongPressEnabled) { 507 if (mNavButtonController != null) { 508 mNavButtonController.setAssistantLongPressEnabled(assistantLongPressEnabled); 509 } 510 } 511 512 /** 513 * Sets the flag indicating setup UI is visible 514 */ setSetupUIVisible(boolean isVisible)515 public void setSetupUIVisible(boolean isVisible) { 516 mSharedState.setupUIVisible = isVisible; 517 if (mTaskbarActivityContext != null) { 518 mTaskbarActivityContext.setSetupUIVisible(isVisible); 519 } 520 } 521 isTaskbarEnabled(DeviceProfile deviceProfile)522 private boolean isTaskbarEnabled(DeviceProfile deviceProfile) { 523 return ENABLE_TASKBAR_NAVBAR_UNIFICATION || deviceProfile.isTaskbarPresent; 524 } 525 onRotationProposal(int rotation, boolean isValid)526 public void onRotationProposal(int rotation, boolean isValid) { 527 if (mTaskbarActivityContext != null) { 528 mTaskbarActivityContext.onRotationProposal(rotation, isValid); 529 } 530 } 531 disableNavBarElements(int displayId, int state1, int state2, boolean animate)532 public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) { 533 mSharedState.disableNavBarDisplayId = displayId; 534 mSharedState.disableNavBarState1 = state1; 535 mSharedState.disableNavBarState2 = state2; 536 if (mTaskbarActivityContext != null) { 537 mTaskbarActivityContext.disableNavBarElements(displayId, state1, state2, animate); 538 } 539 } 540 onSystemBarAttributesChanged(int displayId, int behavior)541 public void onSystemBarAttributesChanged(int displayId, int behavior) { 542 mSharedState.systemBarAttrsDisplayId = displayId; 543 mSharedState.systemBarAttrsBehavior = behavior; 544 if (mTaskbarActivityContext != null) { 545 mTaskbarActivityContext.onSystemBarAttributesChanged(displayId, behavior); 546 } 547 } 548 onNavButtonsDarkIntensityChanged(float darkIntensity)549 public void onNavButtonsDarkIntensityChanged(float darkIntensity) { 550 mSharedState.navButtonsDarkIntensity = darkIntensity; 551 if (mTaskbarActivityContext != null) { 552 mTaskbarActivityContext.onNavButtonsDarkIntensityChanged(darkIntensity); 553 } 554 } 555 onNavigationBarLumaSamplingEnabled(int displayId, boolean enable)556 public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) { 557 mSharedState.mLumaSamplingDisplayId = displayId; 558 mSharedState.mIsLumaSamplingEnabled = enable; 559 if (mTaskbarActivityContext != null) { 560 mTaskbarActivityContext.onNavigationBarLumaSamplingEnabled(displayId, enable); 561 } 562 } 563 removeActivityCallbacksAndListeners()564 private void removeActivityCallbacksAndListeners() { 565 if (mActivity != null) { 566 mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); 567 Log.d(TASKBAR_NOT_DESTROYED_TAG, 568 "unregistering activity lifecycle callbacks from " 569 + "removeActivityCallbackAndListeners()."); 570 mActivity.removeEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback); 571 UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = 572 getUnfoldTransitionProgressProviderForActivity(mActivity); 573 if (unfoldTransitionProgressProvider != null) { 574 unfoldTransitionProgressProvider.removeCallback(mUnfoldTransitionProgressListener); 575 } 576 } 577 } 578 579 /** 580 * Called when the manager is no longer needed 581 */ destroy()582 public void destroy() { 583 debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()"); 584 removeActivityCallbacksAndListeners(); 585 UI_HELPER_EXECUTOR.execute( 586 () -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext)); 587 destroyExistingTaskbar(); 588 removeTaskbarRootViewFromWindow(); 589 if (mUserUnlocked) { 590 DisplayController.INSTANCE.get(mContext).removeChangeListener(mRecreationListener); 591 } 592 SettingsCache.INSTANCE.get(mContext) 593 .unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener); 594 SettingsCache.INSTANCE.get(mContext) 595 .unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener); 596 Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy()."); 597 mContext.unregisterComponentCallbacks(mComponentCallbacks); 598 mContext.unregisterReceiver(mShutdownReceiver); 599 } 600 getCurrentActivityContext()601 public @Nullable TaskbarActivityContext getCurrentActivityContext() { 602 return mTaskbarActivityContext; 603 } 604 dumpLogs(String prefix, PrintWriter pw)605 public void dumpLogs(String prefix, PrintWriter pw) { 606 pw.println(prefix + "TaskbarManager:"); 607 if (mTaskbarActivityContext == null) { 608 pw.println(prefix + "\tTaskbarActivityContext: null"); 609 } else { 610 mTaskbarActivityContext.dumpLogs(prefix + "\t", pw); 611 } 612 } 613 614 @VisibleForTesting addTaskbarRootViewToWindow()615 void addTaskbarRootViewToWindow() { 616 if (enableTaskbarNoRecreate() && !mAddedWindow && mTaskbarActivityContext != null) { 617 mWindowManager.addView(mTaskbarRootLayout, 618 mTaskbarActivityContext.getWindowLayoutParams()); 619 mAddedWindow = true; 620 } 621 } 622 623 @VisibleForTesting removeTaskbarRootViewFromWindow()624 void removeTaskbarRootViewFromWindow() { 625 if (enableTaskbarNoRecreate() && mAddedWindow) { 626 mWindowManager.removeViewImmediate(mTaskbarRootLayout); 627 mAddedWindow = false; 628 } 629 } 630 631 /** Temp logs for b/254119092. */ debugWhyTaskbarNotDestroyed(String debugReason)632 public void debugWhyTaskbarNotDestroyed(String debugReason) { 633 StringJoiner log = new StringJoiner("\n"); 634 log.add(debugReason); 635 636 boolean activityTaskbarPresent = mActivity != null 637 && mActivity.getDeviceProfile().isTaskbarPresent; 638 boolean contextTaskbarPresent = mUserUnlocked 639 && LauncherAppState.getIDP(mContext).getDeviceProfile(mContext).isTaskbarPresent; 640 if (activityTaskbarPresent == contextTaskbarPresent) { 641 log.add("mActivity and mContext agree taskbarIsPresent=" + contextTaskbarPresent); 642 Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString()); 643 return; 644 } 645 646 log.add("mActivity and mContext device profiles have different values, add more logs."); 647 648 log.add("\tmActivity logs:"); 649 log.add("\t\tmActivity=" + mActivity); 650 if (mActivity != null) { 651 log.add("\t\tmActivity.getResources().getConfiguration()=" 652 + mActivity.getResources().getConfiguration()); 653 log.add("\t\tmActivity.getDeviceProfile().isTaskbarPresent=" 654 + activityTaskbarPresent); 655 } 656 log.add("\tmContext logs:"); 657 log.add("\t\tmContext=" + mContext); 658 log.add("\t\tmContext.getResources().getConfiguration()=" 659 + mContext.getResources().getConfiguration()); 660 if (mUserUnlocked) { 661 log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(mContext).isTaskbarPresent=" 662 + contextTaskbarPresent); 663 } else { 664 log.add("\t\tCouldn't get DeviceProfile because !mUserUnlocked"); 665 } 666 667 Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString()); 668 } 669 670 private final DeviceProfile.OnDeviceProfileChangeListener mDebugActivityDeviceProfileChanged = 671 dp -> debugWhyTaskbarNotDestroyed("mActivity onDeviceProfileChanged"); 672 } 673