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