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.util.DisplayController.CHANGE_DENSITY; 24 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; 25 import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG; 26 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 27 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; 28 29 import android.annotation.SuppressLint; 30 import android.app.PendingIntent; 31 import android.content.ComponentCallbacks; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.ActivityInfo; 36 import android.content.res.Configuration; 37 import android.hardware.display.DisplayManager; 38 import android.net.Uri; 39 import android.os.Handler; 40 import android.os.SystemProperties; 41 import android.provider.Settings; 42 import android.util.Log; 43 import android.view.Display; 44 45 import androidx.annotation.NonNull; 46 import androidx.annotation.Nullable; 47 import androidx.annotation.VisibleForTesting; 48 49 import com.android.launcher3.DeviceProfile; 50 import com.android.launcher3.LauncherAppState; 51 import com.android.launcher3.anim.AnimatorPlaybackController; 52 import com.android.launcher3.statemanager.StatefulActivity; 53 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider; 54 import com.android.launcher3.uioverrides.QuickstepLauncher; 55 import com.android.launcher3.util.DisplayController; 56 import com.android.launcher3.util.NavigationMode; 57 import com.android.launcher3.util.SettingsCache; 58 import com.android.launcher3.util.SimpleBroadcastReceiver; 59 import com.android.quickstep.RecentsActivity; 60 import com.android.quickstep.SystemUiProxy; 61 import com.android.quickstep.TouchInteractionService; 62 import com.android.systemui.shared.system.QuickStepContract; 63 import com.android.systemui.unfold.UnfoldTransitionProgressProvider; 64 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; 65 66 import java.io.PrintWriter; 67 import java.util.StringJoiner; 68 69 /** 70 * Class to manage taskbar lifecycle 71 */ 72 public class TaskbarManager { 73 private static final String TAG = "TaskbarManager"; 74 private static final boolean DEBUG = false; 75 76 public static final boolean FLAG_HIDE_NAVBAR_WINDOW = 77 SystemProperties.getBoolean("persist.wm.debug.hide_navbar_window", false); 78 79 private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor( 80 Settings.Secure.USER_SETUP_COMPLETE); 81 82 private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor( 83 Settings.Secure.NAV_BAR_KIDS_MODE); 84 85 private final Context mContext; 86 private final DisplayController mDisplayController; 87 private final TaskbarNavButtonController mNavButtonController; 88 private final SettingsCache.OnChangeListener mUserSetupCompleteListener; 89 private final SettingsCache.OnChangeListener mNavBarKidsModeListener; 90 private final ComponentCallbacks mComponentCallbacks; 91 private final SimpleBroadcastReceiver mShutdownReceiver; 92 93 // The source for this provider is set when Launcher is available 94 // We use 'non-destroyable' version here so the original provider won't be destroyed 95 // as it is tied to the activity lifecycle, not the taskbar lifecycle. 96 // It's destruction/creation will be managed by the activity. 97 private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider = 98 new NonDestroyableScopedUnfoldTransitionProgressProvider(); 99 private NavigationMode mNavMode; 100 101 private TaskbarActivityContext mTaskbarActivityContext; 102 private StatefulActivity mActivity; 103 /** 104 * Cache a copy here so we can initialize state whenever taskbar is recreated, since 105 * this class does not get re-initialized w/ new taskbars. 106 */ 107 private final TaskbarSharedState mSharedState = new TaskbarSharedState(); 108 109 /** 110 * We use WindowManager's ComponentCallbacks() for most of the config changes, however for 111 * navigation mode, that callback gets called too soon, before it's internal navigation mode 112 * reflects the current one. 113 * DisplayController's callback is delayed enough to get the correct nav mode value 114 * 115 * We also use density change here because DeviceProfile has had a chance to update it's state 116 * whereas density for component callbacks registered in this class don't update DeviceProfile. 117 * Confused? Me too. Make it less confusing (TODO: b/227669780) 118 * 119 * Flags used with {@link #mDispInfoChangeListener} 120 */ 121 private static final int CHANGE_FLAGS = CHANGE_NAVIGATION_MODE | CHANGE_DENSITY; 122 private final DisplayController.DisplayInfoChangeListener mDispInfoChangeListener; 123 124 private boolean mUserUnlocked = false; 125 126 public static final int SYSTEM_ACTION_ID_TASKBAR = 499; 127 128 /** 129 * For Taskbar broadcast intent filter. 130 */ 131 public static final String ACTION_SHOW_TASKBAR = "ACTION_SHOW_TASKBAR"; 132 133 private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver = 134 new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast); 135 136 @SuppressLint("WrongConstant") TaskbarManager(TouchInteractionService service)137 public TaskbarManager(TouchInteractionService service) { 138 mDisplayController = DisplayController.INSTANCE.get(service); 139 Display display = 140 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); 141 mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null); 142 mNavButtonController = new TaskbarNavButtonController(service, 143 SystemUiProxy.INSTANCE.get(mContext), new Handler()); 144 mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar(); 145 mNavBarKidsModeListener = isNavBarKidsMode -> recreateTaskbar(); 146 // TODO(b/227669780): Consolidate this w/ DisplayController callbacks 147 mComponentCallbacks = new ComponentCallbacks() { 148 private Configuration mOldConfig = mContext.getResources().getConfiguration(); 149 150 @Override 151 public void onConfigurationChanged(Configuration newConfig) { 152 debugWhyTaskbarNotDestroyed( 153 "TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig); 154 DeviceProfile dp = mUserUnlocked 155 ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) 156 : null; 157 int configDiff = mOldConfig.diff(newConfig); 158 int configDiffForRecreate = configDiff; 159 int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS 160 | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE 161 | ActivityInfo.CONFIG_SCREEN_SIZE; 162 if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0 163 && mTaskbarActivityContext != null && dp != null 164 && !isPhoneMode(dp)) { 165 // Additional check since this callback gets fired multiple times w/o 166 // screen size changing, or when simply rotating the device. 167 // In the case of phone device rotation, we do want to call recreateTaskbar() 168 DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile(); 169 boolean isOrientationChange = 170 (configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0; 171 int oldWidth = isOrientationChange ? oldDp.heightPx : oldDp.widthPx; 172 int oldHeight = isOrientationChange ? oldDp.widthPx : oldDp.heightPx; 173 if (dp.widthPx == oldWidth && dp.heightPx == oldHeight) { 174 configDiffForRecreate &= ~ActivityInfo.CONFIG_SCREEN_SIZE; 175 } 176 } 177 if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) { 178 // Only recreate for theme changes, not other UI mode changes such as docking. 179 int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); 180 int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); 181 if (oldUiNightMode == newUiNightMode) { 182 configDiffForRecreate &= ~ActivityInfo.CONFIG_UI_MODE; 183 } 184 } 185 186 debugWhyTaskbarNotDestroyed("ComponentCallbacks#onConfigurationChanged() " 187 + "configDiffForRecreate=" 188 + Configuration.configurationDiffToString(configDiffForRecreate)); 189 if ((configDiffForRecreate & configsRequiringRecreate) != 0) { 190 recreateTaskbar(); 191 } else { 192 // Config change might be handled without re-creating the taskbar 193 if (mTaskbarActivityContext != null) { 194 if (dp != null && !isTaskbarPresent(dp)) { 195 destroyExistingTaskbar(); 196 } else { 197 if (dp != null && isTaskbarPresent(dp)) { 198 mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); 199 } 200 mTaskbarActivityContext.onConfigurationChanged(configDiff); 201 } 202 } 203 } 204 mOldConfig = newConfig; 205 } 206 207 @Override 208 public void onLowMemory() { } 209 }; 210 mShutdownReceiver = new SimpleBroadcastReceiver(i -> 211 destroyExistingTaskbar()); 212 mDispInfoChangeListener = (context, info, flags) -> { 213 if ((flags & CHANGE_FLAGS) != 0) { 214 mNavMode = info.navigationMode; 215 recreateTaskbar(); 216 } 217 debugWhyTaskbarNotDestroyed("DisplayInfoChangeListener#" 218 + mDisplayController.getChangeFlagsString(flags)); 219 }; 220 mNavMode = mDisplayController.getInfo().navigationMode; 221 mDisplayController.addChangeListener(mDispInfoChangeListener); 222 SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI, 223 mUserSetupCompleteListener); 224 SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE, 225 mNavBarKidsModeListener); 226 mContext.registerComponentCallbacks(mComponentCallbacks); 227 mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN); 228 UI_HELPER_EXECUTOR.execute(() -> { 229 mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast( 230 mContext, 231 SYSTEM_ACTION_ID_TASKBAR, 232 new Intent(ACTION_SHOW_TASKBAR).setPackage(mContext.getPackageName()), 233 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 234 mContext.registerReceiver( 235 mTaskbarBroadcastReceiver, 236 new IntentFilter(ACTION_SHOW_TASKBAR), 237 RECEIVER_NOT_EXPORTED); 238 }); 239 240 debugWhyTaskbarNotDestroyed("TaskbarManager created"); 241 recreateTaskbar(); 242 } 243 destroyExistingTaskbar()244 private void destroyExistingTaskbar() { 245 debugWhyTaskbarNotDestroyed("destroyExistingTaskbar: " + mTaskbarActivityContext); 246 if (mTaskbarActivityContext != null) { 247 mTaskbarActivityContext.onDestroy(); 248 if (!FLAG_HIDE_NAVBAR_WINDOW) { 249 mTaskbarActivityContext = null; 250 } 251 } 252 } 253 254 /** 255 * Show Taskbar upon receiving broadcast 256 */ showTaskbarFromBroadcast(Intent intent)257 private void showTaskbarFromBroadcast(Intent intent) { 258 if (ACTION_SHOW_TASKBAR.equals(intent.getAction()) && mTaskbarActivityContext != null) { 259 mTaskbarActivityContext.showTaskbarFromBroadcast(); 260 } 261 } 262 263 /** 264 * Displays a frame of the first Launcher reveal animation. 265 * 266 * This should be used to run a first Launcher reveal animation whose progress matches a swipe 267 * progress. 268 */ createLauncherStartFromSuwAnim(int duration)269 public AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) { 270 return mTaskbarActivityContext == null 271 ? null : mTaskbarActivityContext.createLauncherStartFromSuwAnim(duration); 272 } 273 274 /** 275 * Called when the user is unlocked 276 */ onUserUnlocked()277 public void onUserUnlocked() { 278 mUserUnlocked = true; 279 recreateTaskbar(); 280 } 281 282 /** 283 * Sets a {@link StatefulActivity} to act as taskbar callback 284 */ setActivity(@onNull StatefulActivity activity)285 public void setActivity(@NonNull StatefulActivity activity) { 286 if (mActivity == activity) { 287 return; 288 } 289 if (mActivity != null) { 290 mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); 291 } 292 mActivity = activity; 293 debugWhyTaskbarNotDestroyed("Set mActivity=" + mActivity); 294 mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); 295 UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = 296 getUnfoldTransitionProgressProviderForActivity(activity); 297 mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider); 298 299 if (mTaskbarActivityContext != null) { 300 mTaskbarActivityContext.setUIController( 301 createTaskbarUIControllerForActivity(mActivity)); 302 } 303 } 304 305 /** 306 * Returns an {@link UnfoldTransitionProgressProvider} to use while the given StatefulActivity 307 * is active. 308 */ getUnfoldTransitionProgressProviderForActivity( StatefulActivity activity)309 private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity( 310 StatefulActivity activity) { 311 if (activity instanceof QuickstepLauncher) { 312 return ((QuickstepLauncher) activity).getUnfoldTransitionProgressProvider(); 313 } 314 return null; 315 } 316 317 /** 318 * Creates a {@link TaskbarUIController} to use while the given StatefulActivity is active. 319 */ createTaskbarUIControllerForActivity(StatefulActivity activity)320 private TaskbarUIController createTaskbarUIControllerForActivity(StatefulActivity activity) { 321 if (activity instanceof QuickstepLauncher) { 322 if (mTaskbarActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) { 323 return new DesktopTaskbarUIController((QuickstepLauncher) activity); 324 } 325 return new LauncherTaskbarUIController((QuickstepLauncher) activity); 326 } 327 if (activity instanceof RecentsActivity) { 328 return new FallbackTaskbarUIController((RecentsActivity) activity); 329 } 330 return TaskbarUIController.DEFAULT; 331 } 332 333 /** 334 * Clears a previously set {@link StatefulActivity} 335 */ clearActivity(@onNull StatefulActivity activity)336 public void clearActivity(@NonNull StatefulActivity activity) { 337 if (mActivity == activity) { 338 mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); 339 mActivity = null; 340 debugWhyTaskbarNotDestroyed("clearActivity"); 341 if (mTaskbarActivityContext != null) { 342 mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT); 343 } 344 mUnfoldProgressProvider.setSourceProvider(null); 345 } 346 } 347 348 /** 349 * This method is called multiple times (ex. initial init, then when user unlocks) in which case 350 * we fully want to destroy an existing taskbar and create a new one. 351 * In other case (folding/unfolding) we don't need to remove and add window. 352 */ 353 @VisibleForTesting recreateTaskbar()354 public void recreateTaskbar() { 355 DeviceProfile dp = mUserUnlocked ? 356 LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null; 357 358 destroyExistingTaskbar(); 359 360 boolean isTaskbarEnabled = dp != null && isTaskbarPresent(dp); 361 debugWhyTaskbarNotDestroyed("recreateTaskbar: isTaskbarEnabled=" + isTaskbarEnabled 362 + " [dp != null (i.e. mUserUnlocked)]=" + (dp != null) 363 + " FLAG_HIDE_NAVBAR_WINDOW=" + FLAG_HIDE_NAVBAR_WINDOW 364 + " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent)); 365 if (!isTaskbarEnabled) { 366 SystemUiProxy.INSTANCE.get(mContext) 367 .notifyTaskbarStatus(/* visible */ false, /* stashed */ false); 368 return; 369 } 370 371 if (mTaskbarActivityContext == null) { 372 mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController, 373 mUnfoldProgressProvider); 374 } else { 375 mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); 376 } 377 mTaskbarActivityContext.init(mSharedState); 378 379 if (mActivity != null) { 380 mTaskbarActivityContext.setUIController( 381 createTaskbarUIControllerForActivity(mActivity)); 382 } 383 } 384 onSystemUiFlagsChanged(int systemUiStateFlags)385 public void onSystemUiFlagsChanged(int systemUiStateFlags) { 386 if (DEBUG) { 387 Log.d(TAG, "SysUI flags changed: " + formatFlagChange(systemUiStateFlags, 388 mSharedState.sysuiStateFlags, QuickStepContract::getSystemUiStateString)); 389 } 390 mSharedState.sysuiStateFlags = systemUiStateFlags; 391 if (mTaskbarActivityContext != null) { 392 mTaskbarActivityContext.updateSysuiStateFlags(systemUiStateFlags, false /* fromInit */); 393 } 394 } 395 onLongPressHomeEnabled(boolean assistantLongPressEnabled)396 public void onLongPressHomeEnabled(boolean assistantLongPressEnabled) { 397 if (mNavButtonController != null) { 398 mNavButtonController.setAssistantLongPressEnabled(assistantLongPressEnabled); 399 } 400 } 401 402 /** 403 * Sets the flag indicating setup UI is visible 404 */ setSetupUIVisible(boolean isVisible)405 public void setSetupUIVisible(boolean isVisible) { 406 mSharedState.setupUIVisible = isVisible; 407 if (mTaskbarActivityContext != null) { 408 mTaskbarActivityContext.setSetupUIVisible(isVisible); 409 } 410 } 411 412 /** 413 * @return {@code true} if provided device profile isn't a large screen profile 414 * and we are using a single window for taskbar and navbar. 415 */ isPhoneMode(DeviceProfile deviceProfile)416 public static boolean isPhoneMode(DeviceProfile deviceProfile) { 417 return TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW && deviceProfile.isPhone; 418 } 419 420 /** 421 * @return {@code true} if {@link #isPhoneMode(DeviceProfile)} is true and we're using 422 * 3 button-nav 423 */ isPhoneButtonNavMode(TaskbarActivityContext context)424 public static boolean isPhoneButtonNavMode(TaskbarActivityContext context) { 425 return isPhoneMode(context.getDeviceProfile()) && context.isThreeButtonNav(); 426 } 427 isTaskbarPresent(DeviceProfile deviceProfile)428 private boolean isTaskbarPresent(DeviceProfile deviceProfile) { 429 return FLAG_HIDE_NAVBAR_WINDOW || deviceProfile.isTaskbarPresent; 430 } 431 onRotationProposal(int rotation, boolean isValid)432 public void onRotationProposal(int rotation, boolean isValid) { 433 if (mTaskbarActivityContext != null) { 434 mTaskbarActivityContext.onRotationProposal(rotation, isValid); 435 } 436 } 437 disableNavBarElements(int displayId, int state1, int state2, boolean animate)438 public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) { 439 mSharedState.disableNavBarDisplayId = displayId; 440 mSharedState.disableNavBarState1 = state1; 441 mSharedState.disableNavBarState2 = state2; 442 if (mTaskbarActivityContext != null) { 443 mTaskbarActivityContext.disableNavBarElements(displayId, state1, state2, animate); 444 } 445 } 446 onSystemBarAttributesChanged(int displayId, int behavior)447 public void onSystemBarAttributesChanged(int displayId, int behavior) { 448 mSharedState.systemBarAttrsDisplayId = displayId; 449 mSharedState.systemBarAttrsBehavior = behavior; 450 if (mTaskbarActivityContext != null) { 451 mTaskbarActivityContext.onSystemBarAttributesChanged(displayId, behavior); 452 } 453 } 454 onNavButtonsDarkIntensityChanged(float darkIntensity)455 public void onNavButtonsDarkIntensityChanged(float darkIntensity) { 456 mSharedState.navButtonsDarkIntensity = darkIntensity; 457 if (mTaskbarActivityContext != null) { 458 mTaskbarActivityContext.onNavButtonsDarkIntensityChanged(darkIntensity); 459 } 460 } 461 462 /** 463 * Called when the manager is no longer needed 464 */ destroy()465 public void destroy() { 466 debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()"); 467 if (mActivity != null) { 468 mActivity.removeOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged); 469 } 470 471 UI_HELPER_EXECUTOR.execute( 472 () -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext)); 473 destroyExistingTaskbar(); 474 mDisplayController.removeChangeListener(mDispInfoChangeListener); 475 SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI, 476 mUserSetupCompleteListener); 477 SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE, 478 mNavBarKidsModeListener); 479 mContext.unregisterComponentCallbacks(mComponentCallbacks); 480 mContext.unregisterReceiver(mShutdownReceiver); 481 } 482 getCurrentActivityContext()483 public @Nullable TaskbarActivityContext getCurrentActivityContext() { 484 return mTaskbarActivityContext; 485 } 486 dumpLogs(String prefix, PrintWriter pw)487 public void dumpLogs(String prefix, PrintWriter pw) { 488 pw.println(prefix + "TaskbarManager:"); 489 if (mTaskbarActivityContext == null) { 490 pw.println(prefix + "\tTaskbarActivityContext: null"); 491 } else { 492 mTaskbarActivityContext.dumpLogs(prefix + "\t", pw); 493 } 494 } 495 496 /** Temp logs for b/254119092. */ debugWhyTaskbarNotDestroyed(String debugReason)497 public void debugWhyTaskbarNotDestroyed(String debugReason) { 498 StringJoiner log = new StringJoiner("\n"); 499 log.add(debugReason); 500 501 boolean activityTaskbarPresent = mActivity != null 502 && mActivity.getDeviceProfile().isTaskbarPresent; 503 boolean contextTaskbarPresent = mUserUnlocked 504 && LauncherAppState.getIDP(mContext).getDeviceProfile(mContext).isTaskbarPresent; 505 if (activityTaskbarPresent == contextTaskbarPresent) { 506 log.add("mActivity and mContext agree taskbarIsPresent=" + contextTaskbarPresent); 507 Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString()); 508 return; 509 } 510 511 log.add("mActivity and mContext device profiles have different values, add more logs."); 512 513 log.add("\tmActivity logs:"); 514 log.add("\t\tmActivity=" + mActivity); 515 if (mActivity != null) { 516 log.add("\t\tmActivity.getResources().getConfiguration()=" 517 + mActivity.getResources().getConfiguration()); 518 log.add("\t\tmActivity.getDeviceProfile().isTaskbarPresent=" 519 + activityTaskbarPresent); 520 } 521 log.add("\tmContext logs:"); 522 log.add("\t\tmContext=" + mContext); 523 log.add("\t\tmContext.getResources().getConfiguration()=" 524 + mContext.getResources().getConfiguration()); 525 if (mUserUnlocked) { 526 log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(mContext).isTaskbarPresent=" 527 + contextTaskbarPresent); 528 } else { 529 log.add("\t\tCouldn't get DeviceProfile because !mUserUnlocked"); 530 } 531 532 Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString()); 533 } 534 535 private final DeviceProfile.OnDeviceProfileChangeListener mDebugActivityDeviceProfileChanged = 536 dp -> debugWhyTaskbarNotDestroyed("mActivity onDeviceProfileChanged"); 537 } 538