• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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