• 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.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