| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.launcher3.taskbar; |
| |
| import static android.content.pm.PackageManager.FEATURE_PC; |
| import static android.os.Trace.TRACE_TAG_APP; |
| import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; |
| import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; |
| import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED; |
| |
| import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; |
| import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; |
| import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY; |
| import static com.android.launcher3.Utilities.isRunningInTestHarness; |
| import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN; |
| import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING; |
| import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN; |
| import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW; |
| import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName; |
| import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; |
| import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; |
| |
| import android.animation.AnimatorSet; |
| import android.animation.ValueAnimator; |
| import android.app.ActivityOptions; |
| import android.content.ActivityNotFoundException; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo.Config; |
| import android.content.pm.LauncherApps; |
| import android.content.res.Resources; |
| import android.graphics.Color; |
| import android.graphics.PixelFormat; |
| import android.graphics.Rect; |
| import android.hardware.display.DisplayManager; |
| import android.os.Process; |
| import android.os.Trace; |
| import android.provider.Settings; |
| import android.util.Log; |
| import android.view.Display; |
| import android.view.Gravity; |
| import android.view.RoundedCorner; |
| import android.view.Surface; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.widget.FrameLayout; |
| import android.widget.Toast; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.launcher3.AbstractFloatingView; |
| import com.android.launcher3.BubbleTextView; |
| import com.android.launcher3.DeviceProfile; |
| import com.android.launcher3.LauncherSettings.Favorites; |
| import com.android.launcher3.R; |
| import com.android.launcher3.anim.AnimatorPlaybackController; |
| import com.android.launcher3.config.FeatureFlags; |
| import com.android.launcher3.dot.DotInfo; |
| import com.android.launcher3.folder.Folder; |
| import com.android.launcher3.folder.FolderIcon; |
| import com.android.launcher3.logger.LauncherAtom; |
| import com.android.launcher3.logging.StatsLogManager; |
| import com.android.launcher3.model.data.AppInfo; |
| import com.android.launcher3.model.data.FolderInfo; |
| import com.android.launcher3.model.data.ItemInfo; |
| import com.android.launcher3.model.data.WorkspaceItemInfo; |
| import com.android.launcher3.popup.PopupContainerWithArrow; |
| import com.android.launcher3.popup.PopupDataProvider; |
| import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag; |
| import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback; |
| import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController; |
| import com.android.launcher3.taskbar.bubbles.BubbleBarController; |
| import com.android.launcher3.taskbar.bubbles.BubbleBarView; |
| import com.android.launcher3.taskbar.bubbles.BubbleBarViewController; |
| import com.android.launcher3.taskbar.bubbles.BubbleControllers; |
| import com.android.launcher3.taskbar.bubbles.BubbleDismissController; |
| import com.android.launcher3.taskbar.bubbles.BubbleDragController; |
| import com.android.launcher3.taskbar.bubbles.BubbleStashController; |
| import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController; |
| import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; |
| import com.android.launcher3.testing.TestLogging; |
| import com.android.launcher3.testing.shared.TestProtocol; |
| import com.android.launcher3.touch.ItemClickHandler; |
| import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy; |
| import com.android.launcher3.util.ActivityOptionsWrapper; |
| import com.android.launcher3.util.DisplayController; |
| import com.android.launcher3.util.Executors; |
| import com.android.launcher3.util.NavigationMode; |
| import com.android.launcher3.util.PackageManagerHelper; |
| import com.android.launcher3.util.RunnableList; |
| import com.android.launcher3.util.SettingsCache; |
| import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; |
| import com.android.launcher3.util.TraceHelper; |
| import com.android.launcher3.util.ViewCache; |
| import com.android.launcher3.views.ActivityContext; |
| import com.android.quickstep.views.RecentsView; |
| import com.android.quickstep.views.TaskView; |
| import com.android.systemui.shared.recents.model.Task; |
| import com.android.systemui.shared.rotation.RotationButtonController; |
| import com.android.systemui.shared.system.ActivityManagerWrapper; |
| import com.android.systemui.unfold.updates.RotationChangeProvider; |
| import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; |
| |
| import java.io.PrintWriter; |
| import java.util.Optional; |
| |
| /** |
| * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements |
| * that are used by both Launcher and Taskbar (such as Folder) to reference a generic |
| * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer. |
| */ |
| public class TaskbarActivityContext extends BaseTaskbarContext { |
| |
| private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar"; |
| |
| private static final String TAG = "TaskbarActivityContext"; |
| |
| private static final String WINDOW_TITLE = "Taskbar"; |
| |
| private final TaskbarDragLayer mDragLayer; |
| private final TaskbarControllers mControllers; |
| |
| private final WindowManager mWindowManager; |
| private final @Nullable RoundedCorner mLeftCorner, mRightCorner; |
| private DeviceProfile mDeviceProfile; |
| private WindowManager.LayoutParams mWindowLayoutParams; |
| private boolean mIsFullscreen; |
| // The size we should return to when we call setTaskbarWindowFullscreen(false) |
| private int mLastRequestedNonFullscreenHeight; |
| |
| private NavigationMode mNavMode; |
| private final boolean mImeDrawsImeNavBar; |
| private final ViewCache mViewCache = new ViewCache(); |
| |
| private final boolean mIsSafeModeEnabled; |
| private final boolean mIsUserSetupComplete; |
| private final boolean mIsNavBarForceVisible; |
| private final boolean mIsNavBarKidsMode; |
| |
| private boolean mIsDestroyed = false; |
| // The flag to know if the window is excluded from magnification region computation. |
| private boolean mIsExcludeFromMagnificationRegion = false; |
| private boolean mBindingItems = false; |
| private boolean mAddedWindow = false; |
| |
| // The bounds of the taskbar items relative to TaskbarDragLayer |
| private final Rect mTransientTaskbarBounds = new Rect(); |
| |
| private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate; |
| |
| public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp, |
| TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider |
| unfoldTransitionProgressProvider) { |
| super(windowContext); |
| |
| applyDeviceProfile(launcherDp); |
| |
| final Resources resources = getResources(); |
| |
| mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false); |
| mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", |
| () -> getPackageManager().isSafeMode()); |
| |
| // TODO(b/244231596) For shared Taskbar window, update this value in applyDeviceProfile() |
| // instead so to get correct value when recreating the taskbar |
| SettingsCache settingsCache = SettingsCache.INSTANCE.get(this); |
| mIsUserSetupComplete = settingsCache.getValue( |
| Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0); |
| mIsNavBarKidsMode = settingsCache.getValue( |
| Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0); |
| mIsNavBarForceVisible = mIsNavBarKidsMode; |
| |
| // Get display and corners first, as views might use them in constructor. |
| Display display = windowContext.getDisplay(); |
| Context c = getApplicationContext(); |
| mWindowManager = c.getSystemService(WindowManager.class); |
| mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); |
| mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); |
| |
| // Inflate views. |
| boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile); |
| int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode |
| ? R.layout.transient_taskbar |
| : R.layout.taskbar; |
| mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false); |
| TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view); |
| TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim); |
| FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view); |
| StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle); |
| BubbleBarView bubbleBarView = mDragLayer.findViewById(R.id.taskbar_bubbles); |
| StashedHandleView bubbleHandleView = mDragLayer.findViewById(R.id.stashed_bubble_handle); |
| |
| mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this); |
| |
| final boolean isDesktopMode = getPackageManager().hasSystemFeature(FEATURE_PC); |
| |
| // If Bubble bar is present, TaskbarControllers depends on it so build it first. |
| Optional<BubbleControllers> bubbleControllersOptional = Optional.empty(); |
| if (BubbleBarController.BUBBLE_BAR_ENABLED && bubbleBarView != null) { |
| bubbleControllersOptional = Optional.of(new BubbleControllers( |
| new BubbleBarController(this, bubbleBarView), |
| new BubbleBarViewController(this, bubbleBarView), |
| new BubbleStashController(this), |
| new BubbleStashedHandleViewController(this, bubbleHandleView), |
| new BubbleDragController(this), |
| new BubbleDismissController(this, mDragLayer))); |
| } |
| |
| // Construct controllers. |
| mControllers = new TaskbarControllers(this, |
| new TaskbarDragController(this), |
| buttonController, |
| isDesktopMode |
| ? new DesktopNavbarButtonsViewController(this, navButtonsView) |
| : new NavbarButtonsViewController(this, navButtonsView), |
| new RotationButtonController(this, |
| c.getColor(R.color.floating_rotation_button_light_color), |
| c.getColor(R.color.floating_rotation_button_dark_color), |
| R.drawable.ic_sysbar_rotate_button_ccw_start_0, |
| R.drawable.ic_sysbar_rotate_button_ccw_start_90, |
| R.drawable.ic_sysbar_rotate_button_cw_start_0, |
| R.drawable.ic_sysbar_rotate_button_cw_start_90, |
| () -> getDisplay().getRotation()), |
| new TaskbarDragLayerController(this, mDragLayer), |
| new TaskbarViewController(this, taskbarView), |
| new TaskbarScrimViewController(this, taskbarScrimView), |
| new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider, |
| mWindowManager, |
| new RotationChangeProvider(c.getSystemService(DisplayManager.class), this, |
| getMainThreadHandler())), |
| new TaskbarKeyguardController(this), |
| new StashedHandleViewController(this, stashedHandleView), |
| new TaskbarStashController(this), |
| new TaskbarAutohideSuspendController(this), |
| new TaskbarPopupController(this), |
| new TaskbarForceVisibleImmersiveController(this), |
| new TaskbarOverlayController(this, launcherDp), |
| new TaskbarAllAppsController(), |
| new TaskbarInsetsController(this), |
| new VoiceInteractionWindowController(this), |
| new TaskbarTranslationController(this), |
| new TaskbarSpringOnStashController(this), |
| isDesktopMode |
| ? new DesktopTaskbarRecentAppsController(this) |
| : TaskbarRecentAppsController.DEFAULT, |
| new TaskbarEduTooltipController(this), |
| new KeyboardQuickSwitchController(), |
| new TaskbarDividerPopupController(this), |
| bubbleControllersOptional); |
| } |
| |
| /** Updates {@link DeviceProfile} instances for any Taskbar windows. */ |
| public void updateDeviceProfile(DeviceProfile launcherDp) { |
| applyDeviceProfile(launcherDp); |
| |
| mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp); |
| AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE); |
| // Reapply fullscreen to take potential new screen size into account. |
| setTaskbarWindowFullscreen(mIsFullscreen); |
| |
| dispatchDeviceProfileChanged(); |
| } |
| |
| /** |
| * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update |
| * the icon size |
| */ |
| private void applyDeviceProfile(DeviceProfile originDeviceProfile) { |
| mDeviceProfile = originDeviceProfile.toBuilder(this) |
| .withDimensionsOverride(deviceProfile -> { |
| // Taskbar should match the number of icons of hotseat |
| deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons; |
| // Same QSB width to have a smooth animation |
| deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth; |
| |
| // Update icon size |
| deviceProfile.iconSizePx = deviceProfile.taskbarIconSize; |
| deviceProfile.updateIconSize(1f, getResources()); |
| }).build(); |
| mNavMode = DisplayController.getNavigationMode(this); |
| } |
| |
| |
| public void init(@NonNull TaskbarSharedState sharedState) { |
| mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight(); |
| mWindowLayoutParams = createAllWindowParams(); |
| |
| // Initialize controllers after all are constructed. |
| mControllers.init(sharedState); |
| updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */); |
| disableNavBarElements(sharedState.disableNavBarDisplayId, sharedState.disableNavBarState1, |
| sharedState.disableNavBarState2, false /* animate */); |
| onSystemBarAttributesChanged(sharedState.systemBarAttrsDisplayId, |
| sharedState.systemBarAttrsBehavior); |
| onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity); |
| |
| if (FLAG_HIDE_NAVBAR_WINDOW) { |
| // W/ the flag not set this entire class gets re-created, which resets the value of |
| // mIsDestroyed. We re-use the class for small-screen, so we explicitly have to mark |
| // this class as non-destroyed |
| mIsDestroyed = false; |
| } |
| |
| if (!mAddedWindow) { |
| mWindowManager.addView(mDragLayer, mWindowLayoutParams); |
| mAddedWindow = true; |
| } else { |
| mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); |
| } |
| } |
| |
| /** |
| * Show Taskbar upon receiving broadcast |
| */ |
| public void showTaskbarFromBroadcast() { |
| mControllers.taskbarStashController.showTaskbarFromBroadcast(); |
| } |
| |
| @Override |
| public DeviceProfile getDeviceProfile() { |
| return mDeviceProfile; |
| } |
| |
| @Override |
| public void dispatchDeviceProfileChanged() { |
| super.dispatchDeviceProfileChanged(); |
| Trace.instantForTrack(TRACE_TAG_APP, "TaskbarActivityContext#DeviceProfileChanged", |
| getDeviceProfile().toSmallString()); |
| } |
| |
| /** |
| * Returns the View bounds of transient taskbar. |
| */ |
| public Rect getTransientTaskbarBounds() { |
| return mTransientTaskbarBounds; |
| } |
| |
| @Override |
| public StatsLogManager getStatsLogManager() { |
| // Used to mock, can't mock a default interface method directly |
| return super.getStatsLogManager(); |
| } |
| |
| /** |
| * Creates LayoutParams for adding a view directly to WindowManager as a new window. |
| * @param type The window type to pass to the created WindowManager.LayoutParams. |
| * @param title The window title to pass to the created WindowManager.LayoutParams. |
| */ |
| public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) { |
| int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_SLIPPERY |
| | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; |
| if (DisplayController.isTransientTaskbar(this) && !isRunningInTestHarness()) { |
| windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; |
| } |
| WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams( |
| MATCH_PARENT, |
| mLastRequestedNonFullscreenHeight, |
| type, |
| windowFlags, |
| PixelFormat.TRANSLUCENT); |
| windowLayoutParams.setTitle(title); |
| windowLayoutParams.packageName = getPackageName(); |
| windowLayoutParams.gravity = Gravity.BOTTOM; |
| windowLayoutParams.setFitInsetsTypes(0); |
| windowLayoutParams.receiveInsetsIgnoringZOrder = true; |
| windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; |
| windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| windowLayoutParams.privateFlags = |
| WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; |
| windowLayoutParams.accessibilityTitle = getString( |
| TaskbarManager.isPhoneMode(mDeviceProfile) |
| ? R.string.taskbar_phone_a11y_title |
| : R.string.taskbar_a11y_title); |
| |
| return windowLayoutParams; |
| } |
| |
| /** |
| * Creates {@link WindowManager.LayoutParams} for Taskbar, and also sets LP.paramsForRotation |
| * for taskbar showing as navigation bar |
| */ |
| private WindowManager.LayoutParams createAllWindowParams() { |
| WindowManager.LayoutParams windowLayoutParams = |
| createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL, |
| TaskbarActivityContext.WINDOW_TITLE); |
| boolean isPhoneNavMode = TaskbarManager.isPhoneButtonNavMode(this); |
| if (!isPhoneNavMode) { |
| return windowLayoutParams; |
| } |
| |
| // Provide WM layout params for all rotations to cache, see NavigationBar#getBarLayoutParams |
| int width = WindowManager.LayoutParams.MATCH_PARENT; |
| int height = WindowManager.LayoutParams.MATCH_PARENT; |
| int gravity = Gravity.BOTTOM; |
| windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4]; |
| for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { |
| WindowManager.LayoutParams lp = |
| createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL, |
| TaskbarActivityContext.WINDOW_TITLE); |
| switch (rot) { |
| case Surface.ROTATION_0, Surface.ROTATION_180 -> { |
| // Defaults are fine |
| width = WindowManager.LayoutParams.MATCH_PARENT; |
| height = mLastRequestedNonFullscreenHeight; |
| gravity = Gravity.BOTTOM; |
| } |
| case Surface.ROTATION_90 -> { |
| width = mLastRequestedNonFullscreenHeight; |
| height = WindowManager.LayoutParams.MATCH_PARENT; |
| gravity = Gravity.END; |
| } |
| case Surface.ROTATION_270 -> { |
| width = mLastRequestedNonFullscreenHeight; |
| height = WindowManager.LayoutParams.MATCH_PARENT; |
| gravity = Gravity.START; |
| } |
| |
| } |
| lp.width = width; |
| lp.height = height; |
| lp.gravity = gravity; |
| windowLayoutParams.paramsForRotation[rot] = lp; |
| } |
| |
| // Override current layout params |
| WindowManager.LayoutParams currentParams = |
| windowLayoutParams.paramsForRotation[getDisplay().getRotation()]; |
| windowLayoutParams.width = currentParams.width; |
| windowLayoutParams.height = currentParams.height; |
| windowLayoutParams.gravity = currentParams.gravity; |
| |
| return windowLayoutParams; |
| } |
| |
| public void onConfigurationChanged(@Config int configChanges) { |
| mControllers.onConfigurationChanged(configChanges); |
| if (!mIsUserSetupComplete) { |
| setTaskbarWindowHeight(getSetupWindowHeight()); |
| } |
| } |
| |
| public boolean isThreeButtonNav() { |
| return mNavMode == NavigationMode.THREE_BUTTONS; |
| } |
| |
| public boolean isGestureNav() { |
| return mNavMode == NavigationMode.NO_BUTTON; |
| } |
| |
| public boolean imeDrawsImeNavBar() { |
| return mImeDrawsImeNavBar; |
| } |
| |
| public int getLeftCornerRadius() { |
| return mLeftCorner == null ? 0 : mLeftCorner.getRadius(); |
| } |
| |
| public int getRightCornerRadius() { |
| return mRightCorner == null ? 0 : mRightCorner.getRadius(); |
| } |
| |
| public WindowManager.LayoutParams getWindowLayoutParams() { |
| return mWindowLayoutParams; |
| } |
| |
| @Override |
| public TaskbarDragLayer getDragLayer() { |
| return mDragLayer; |
| } |
| |
| @Override |
| public Rect getFolderBoundingBox() { |
| return mControllers.taskbarDragLayerController.getFolderBoundingBox(); |
| } |
| |
| @Override |
| public TaskbarDragController getDragController() { |
| return mControllers.taskbarDragController; |
| } |
| |
| @Nullable |
| public BubbleControllers getBubbleControllers() { |
| return mControllers.bubbleControllers.orElse(null); |
| } |
| |
| @Override |
| public ViewCache getViewCache() { |
| return mViewCache; |
| } |
| |
| @Override |
| public View.OnClickListener getItemOnClickListener() { |
| return this::onTaskbarIconClicked; |
| } |
| |
| /** |
| * Change from hotseat/predicted hotseat to taskbar container. |
| */ |
| @Override |
| public void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { |
| if (!itemInfoBuilder.hasContainerInfo()) { |
| return; |
| } |
| LauncherAtom.ContainerInfo oldContainer = itemInfoBuilder.getContainerInfo(); |
| |
| LauncherAtom.TaskBarContainer.Builder taskbarBuilder = |
| LauncherAtom.TaskBarContainer.newBuilder(); |
| if (mControllers.uiController.isInOverview()) { |
| taskbarBuilder.setTaskSwitcherContainer( |
| LauncherAtom.TaskSwitcherContainer.newBuilder()); |
| } |
| |
| if (oldContainer.hasPredictedHotseatContainer()) { |
| LauncherAtom.PredictedHotseatContainer predictedHotseat = |
| oldContainer.getPredictedHotseatContainer(); |
| |
| if (predictedHotseat.hasIndex()) { |
| taskbarBuilder.setIndex(predictedHotseat.getIndex()); |
| } |
| if (predictedHotseat.hasCardinality()) { |
| taskbarBuilder.setCardinality(predictedHotseat.getCardinality()); |
| } |
| |
| itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder() |
| .setTaskBarContainer(taskbarBuilder)); |
| } else if (oldContainer.hasHotseat()) { |
| LauncherAtom.HotseatContainer hotseat = oldContainer.getHotseat(); |
| |
| if (hotseat.hasIndex()) { |
| taskbarBuilder.setIndex(hotseat.getIndex()); |
| } |
| |
| itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder() |
| .setTaskBarContainer(taskbarBuilder)); |
| } else if (oldContainer.hasFolder() && oldContainer.getFolder().hasHotseat()) { |
| LauncherAtom.FolderContainer.Builder folderBuilder = oldContainer.getFolder() |
| .toBuilder(); |
| LauncherAtom.HotseatContainer hotseat = folderBuilder.getHotseat(); |
| |
| if (hotseat.hasIndex()) { |
| taskbarBuilder.setIndex(hotseat.getIndex()); |
| } |
| |
| folderBuilder.setTaskbar(taskbarBuilder); |
| folderBuilder.clearHotseat(); |
| itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder() |
| .setFolder(folderBuilder)); |
| } else if (oldContainer.hasAllAppsContainer()) { |
| itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder() |
| .setAllAppsContainer(oldContainer.getAllAppsContainer().toBuilder() |
| .setTaskbarContainer(taskbarBuilder))); |
| } else if (oldContainer.hasPredictionContainer()) { |
| itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder() |
| .setPredictionContainer(oldContainer.getPredictionContainer().toBuilder() |
| .setTaskbarContainer(taskbarBuilder))); |
| } |
| } |
| |
| @Override |
| public DotInfo getDotInfoForItem(ItemInfo info) { |
| return getPopupDataProvider().getDotInfoForItem(info); |
| } |
| |
| @NonNull |
| @Override |
| public PopupDataProvider getPopupDataProvider() { |
| return mControllers.taskbarPopupController.getPopupDataProvider(); |
| } |
| |
| @Override |
| public View.AccessibilityDelegate getAccessibilityDelegate() { |
| return mAccessibilityDelegate; |
| } |
| |
| @Override |
| public boolean isBindingItems() { |
| return mBindingItems; |
| } |
| |
| public void setBindingItems(boolean bindingItems) { |
| mBindingItems = bindingItems; |
| } |
| |
| @Override |
| public void onDragStart() { |
| setTaskbarWindowFullscreen(true); |
| } |
| |
| @Override |
| public void onDragEnd() { |
| onDragEndOrViewRemoved(); |
| } |
| |
| @Override |
| public void onPopupVisibilityChanged(boolean isVisible) { |
| setTaskbarWindowFocusable(isVisible); |
| } |
| |
| @Override |
| public void onSplitScreenMenuButtonClicked() { |
| PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(this); |
| if (popup != null) { |
| popup.addOnCloseCallback(() -> { |
| mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); |
| }); |
| } |
| } |
| |
| @Override |
| public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) { |
| RunnableList callbacks = new RunnableList(); |
| ActivityOptions options = ActivityOptions.makeCustomAnimation( |
| this, 0, 0, Color.TRANSPARENT, |
| Executors.MAIN_EXECUTOR.getHandler(), null, |
| elapsedRealTime -> callbacks.executeAllAndDestroy()); |
| options.setSplashScreenStyle(splashScreenStyle); |
| options.setPendingIntentBackgroundActivityStartMode( |
| ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); |
| return new ActivityOptionsWrapper(options, callbacks); |
| } |
| |
| @Override |
| public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) { |
| return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED); |
| } |
| |
| /** |
| * Sets a new data-source for this taskbar instance |
| */ |
| public void setUIController(@NonNull TaskbarUIController uiController) { |
| mControllers.setUiController(uiController); |
| } |
| |
| /** |
| * Sets the flag indicating setup UI is visible |
| */ |
| public void setSetupUIVisible(boolean isVisible) { |
| mControllers.taskbarStashController.setSetupUIVisible(isVisible); |
| } |
| |
| /** |
| * Called when this instance of taskbar is no longer needed |
| */ |
| public void onDestroy() { |
| mIsDestroyed = true; |
| setUIController(TaskbarUIController.DEFAULT); |
| mControllers.onDestroy(); |
| if (!FLAG_HIDE_NAVBAR_WINDOW) { |
| mWindowManager.removeViewImmediate(mDragLayer); |
| mAddedWindow = false; |
| } |
| } |
| |
| public boolean isDestroyed() { |
| return mIsDestroyed; |
| } |
| |
| public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) { |
| mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags, |
| fromInit); |
| boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0; |
| onNotificationShadeExpandChanged(isShadeVisible, fromInit); |
| mControllers.taskbarViewController.setRecentsButtonDisabled( |
| mControllers.navbarButtonsViewController.isRecentsDisabled() |
| || isNavBarKidsModeActive()); |
| mControllers.stashedHandleViewController.setIsHomeButtonDisabled( |
| mControllers.navbarButtonsViewController.isHomeDisabled()); |
| mControllers.stashedHandleViewController.updateStateForSysuiFlags(systemUiStateFlags); |
| mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags); |
| mControllers.taskbarStashController.updateStateForSysuiFlags( |
| systemUiStateFlags, fromInit || !isUserSetupComplete()); |
| mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags, |
| fromInit); |
| mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags); |
| mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags); |
| mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible( |
| (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit); |
| mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags); |
| mControllers.bubbleControllers.ifPresent(controllers -> { |
| controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags); |
| controllers.bubbleStashedHandleViewController.setIsHomeButtonDisabled( |
| mControllers.navbarButtonsViewController.isHomeDisabled()); |
| }); |
| } |
| |
| /** |
| * Hides the taskbar icons and background when the notication shade is expanded. |
| */ |
| private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) { |
| float alpha = isExpanded ? 0 : 1; |
| AnimatorSet anim = new AnimatorSet(); |
| anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().get( |
| TaskbarViewController.ALPHA_INDEX_NOTIFICATION_EXPANDED).animateToValue(alpha)); |
| anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar() |
| .animateToValue(alpha)); |
| anim.start(); |
| if (skipAnim) { |
| anim.end(); |
| } |
| } |
| |
| public void onRotationProposal(int rotation, boolean isValid) { |
| mControllers.rotationButtonController.onRotationProposal(rotation, isValid); |
| } |
| |
| public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) { |
| if (displayId != getDisplayId()) { |
| return; |
| } |
| mControllers.rotationButtonController.onDisable2FlagChanged(state2); |
| } |
| |
| public void onSystemBarAttributesChanged(int displayId, int behavior) { |
| mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior); |
| } |
| |
| public void onNavButtonsDarkIntensityChanged(float darkIntensity) { |
| mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity() |
| .updateValue(darkIntensity); |
| } |
| |
| /** |
| * Called to update a {@link AutohideSuspendFlag} with a new value. |
| */ |
| public void setAutohideSuspendFlag(@AutohideSuspendFlag int flag, boolean newValue) { |
| mControllers.taskbarAutohideSuspendController.updateFlag(flag, newValue); |
| } |
| |
| /** |
| * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size. |
| */ |
| public void setTaskbarWindowFullscreen(boolean fullscreen) { |
| setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen); |
| mIsFullscreen = fullscreen; |
| setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight); |
| } |
| |
| /** |
| * Called when drag ends or when a view is removed from the DragLayer. |
| */ |
| void onDragEndOrViewRemoved() { |
| boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress(); |
| |
| // Overlay AFVs are in a separate window and do not require Taskbar to be fullscreen. |
| if (!isDragInProgress |
| && !AbstractFloatingView.hasOpenView( |
| this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) { |
| // Reverts Taskbar window to its original size |
| setTaskbarWindowFullscreen(false); |
| } |
| |
| setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, isDragInProgress); |
| } |
| |
| public boolean isTaskbarWindowFullscreen() { |
| return mIsFullscreen; |
| } |
| |
| /** |
| * Updates the TaskbarContainer height (pass {@link #getDefaultTaskbarWindowHeight()} to reset). |
| */ |
| public void setTaskbarWindowHeight(int height) { |
| if (mWindowLayoutParams.height == height || mIsDestroyed) { |
| return; |
| } |
| if (height == MATCH_PARENT) { |
| height = mDeviceProfile.heightPx; |
| } else { |
| mLastRequestedNonFullscreenHeight = height; |
| if (mIsFullscreen) { |
| // We still need to be fullscreen, so defer any change to our height until we call |
| // setTaskbarWindowFullscreen(false). For example, this could happen when dragging |
| // from the gesture region, as the drag will cancel the gesture and reset launcher's |
| // state, which in turn normally would reset the taskbar window height as well. |
| return; |
| } |
| } |
| mWindowLayoutParams.height = height; |
| mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged(); |
| mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); |
| } |
| |
| /** |
| * Returns the default height of the window, including the static corner radii above taskbar. |
| */ |
| public int getDefaultTaskbarWindowHeight() { |
| Resources resources = getResources(); |
| |
| if (FLAG_HIDE_NAVBAR_WINDOW && mDeviceProfile.isPhone) { |
| return isThreeButtonNav() ? |
| resources.getDimensionPixelSize(R.dimen.taskbar_size) : |
| resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size); |
| } |
| |
| if (!isUserSetupComplete()) { |
| return getSetupWindowHeight(); |
| } |
| |
| if (DisplayController.isTransientTaskbar(this)) { |
| return mDeviceProfile.taskbarHeight |
| + (2 * mDeviceProfile.taskbarBottomMargin) |
| + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur); |
| } |
| |
| return mDeviceProfile.taskbarHeight |
| + Math.max(getLeftCornerRadius(), getRightCornerRadius()); |
| } |
| |
| public int getSetupWindowHeight() { |
| return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame); |
| } |
| |
| /** |
| * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar |
| * window. |
| */ |
| public void setTaskbarWindowFocusable(boolean focusable) { |
| if (focusable) { |
| mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE; |
| } else { |
| mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE; |
| } |
| mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); |
| } |
| |
| /** |
| * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar |
| * window. If we're now focusable, also move nav buttons to a separate window above IME. |
| */ |
| public void setTaskbarWindowFocusableForIme(boolean focusable) { |
| if (focusable) { |
| mControllers.navbarButtonsViewController.moveNavButtonsToNewWindow(); |
| } else { |
| mControllers.navbarButtonsViewController.moveNavButtonsBackToTaskbarWindow(); |
| } |
| setTaskbarWindowFocusable(focusable); |
| } |
| |
| /** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */ |
| public void addWindowView(View view, WindowManager.LayoutParams windowLayoutParams) { |
| if (!view.isAttachedToWindow()) { |
| mWindowManager.addView(view, windowLayoutParams); |
| } |
| } |
| |
| /** Removes the given view from WindowManager. See {@link #addWindowView}. */ |
| public void removeWindowView(View view) { |
| if (view.isAttachedToWindow()) { |
| mWindowManager.removeViewImmediate(view); |
| } |
| } |
| |
| @Override |
| public void startSplitSelection(SplitSelectSource splitSelectSource) { |
| mControllers.uiController.startSplitSelection(splitSelectSource); |
| } |
| |
| protected void onTaskbarIconClicked(View view) { |
| boolean shouldCloseAllOpenViews = true; |
| Object tag = view.getTag(); |
| if (tag instanceof Task) { |
| Task task = (Task) tag; |
| ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, |
| ActivityOptions.makeBasic()); |
| mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); |
| } else if (tag instanceof FolderInfo) { |
| shouldCloseAllOpenViews = false; |
| FolderIcon folderIcon = (FolderIcon) view; |
| Folder folder = folderIcon.getFolder(); |
| |
| folder.setOnFolderStateChangedListener(newState -> { |
| if (newState == Folder.STATE_OPEN) { |
| setTaskbarWindowFocusableForIme(true); |
| } else if (newState == Folder.STATE_CLOSED) { |
| // Defer by a frame to ensure we're no longer fullscreen and thus won't jump. |
| getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false)); |
| folder.setOnFolderStateChangedListener(null); |
| } |
| }); |
| |
| setTaskbarWindowFullscreen(true); |
| |
| getDragLayer().post(() -> { |
| folder.animateOpen(); |
| getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN); |
| |
| folder.iterateOverItems((itemInfo, itemView) -> { |
| mControllers.taskbarViewController |
| .setClickAndLongClickListenersForIcon(itemView); |
| // To play haptic when dragging, like other Taskbar items do. |
| itemView.setHapticFeedbackEnabled(true); |
| return false; |
| }); |
| }); |
| } else if (tag instanceof WorkspaceItemInfo) { |
| // Tapping a launchable icon on Taskbar |
| WorkspaceItemInfo info = (WorkspaceItemInfo) tag; |
| if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) { |
| TaskbarUIController taskbarUIController = mControllers.uiController; |
| RecentsView recents = taskbarUIController.getRecentsView(); |
| if (recents != null && recents.isSplitSelectionActive()) { |
| // If we are selecting a second app for split, launch the split tasks |
| taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); |
| } else { |
| // Else launch the selected task |
| Intent intent = new Intent(info.getIntent()) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { |
| Toast.makeText(this, R.string.safemode_shortcut_error, |
| Toast.LENGTH_SHORT).show(); |
| } else if (info.isPromise()) { |
| TestLogging.recordEvent( |
| TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon"); |
| intent = new PackageManagerHelper(this) |
| .getMarketIntent(info.getTargetPackage()) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| startActivity(intent); |
| |
| } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { |
| TestLogging.recordEvent( |
| TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut"); |
| String id = info.getDeepShortcutId(); |
| String packageName = intent.getPackage(); |
| getSystemService(LauncherApps.class) |
| .startShortcut(packageName, id, null, null, info.user); |
| } else { |
| launchFromTaskbarPreservingSplitIfVisible(recents, info); |
| } |
| |
| } catch (NullPointerException |
| | ActivityNotFoundException |
| | SecurityException e) { |
| Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) |
| .show(); |
| Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); |
| return; |
| } |
| |
| } |
| mControllers.uiController.onTaskbarIconLaunched(info); |
| mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); |
| } |
| } else if (tag instanceof AppInfo) { |
| // Tapping an item in AllApps |
| AppInfo info = (AppInfo) tag; |
| TaskbarUIController taskbarUIController = mControllers.uiController; |
| RecentsView recents = taskbarUIController.getRecentsView(); |
| if (recents != null |
| && taskbarUIController.getRecentsView().isSplitSelectionActive()) { |
| // If we are selecting a second app for split, launch the split tasks |
| taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); |
| } else { |
| launchFromTaskbarPreservingSplitIfVisible(recents, info); |
| } |
| mControllers.uiController.onTaskbarIconLaunched(info); |
| mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); |
| } else if (tag instanceof ItemClickProxy) { |
| ((ItemClickProxy) tag).onItemClicked(view); |
| } else { |
| Log.e(TAG, "Unknown type clicked: " + tag); |
| } |
| |
| if (shouldCloseAllOpenViews) { |
| AbstractFloatingView.closeAllOpenViews(this); |
| } |
| } |
| |
| /** |
| * Run when the user taps a Taskbar icon while in Overview. If the tapped app is currently |
| * visible to the user in Overview, or is part of a visible split pair, we expand the TaskView |
| * as if the user tapped on it (preserving the split pair). Otherwise, launch it normally |
| * (potentially breaking a split pair). |
| */ |
| private void launchFromTaskbarPreservingSplitIfVisible(@Nullable RecentsView recents, |
| ItemInfo info) { |
| if (recents == null) { |
| return; |
| } |
| recents.getSplitSelectController().findLastActiveTaskAndRunCallback( |
| info.getComponentKey(), |
| foundTask -> { |
| if (foundTask != null) { |
| TaskView foundTaskView = |
| recents.getTaskViewByTaskId(foundTask.key.id); |
| if (foundTaskView != null |
| && foundTaskView.isVisibleToUser()) { |
| TestLogging.recordEvent( |
| TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon"); |
| foundTaskView.launchTasks(); |
| return; |
| } |
| } |
| startItemInfoActivity(info); |
| }); |
| } |
| |
| private void startItemInfoActivity(ItemInfo info) { |
| Intent intent = new Intent(info.getIntent()) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon"); |
| if (info.user.equals(Process.myUserHandle())) { |
| // TODO(b/216683257): Use startActivityForResult for search results that require it. |
| startActivity(intent); |
| } else { |
| getSystemService(LauncherApps.class).startMainActivity( |
| intent.getComponent(), info.user, intent.getSourceBounds(), null); |
| } |
| } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { |
| Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) |
| .show(); |
| Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); |
| } |
| } |
| |
| /** |
| * Returns whether the taskbar is currently visually stashed. |
| */ |
| public boolean isTaskbarStashed() { |
| return mControllers.taskbarStashController.isStashed(); |
| } |
| |
| /** |
| * Called when we detect a long press in the nav region before passing the gesture slop. |
| * @return Whether taskbar handled the long press, and thus should cancel the gesture. |
| */ |
| public boolean onLongPressToUnstashTaskbar() { |
| return mControllers.taskbarStashController.onLongPressToUnstashTaskbar(); |
| } |
| |
| /** |
| * Called when we want to unstash taskbar when user performs swipes up gesture. |
| */ |
| public void onSwipeToUnstashTaskbar() { |
| mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false); |
| mControllers.taskbarEduTooltipController.hide(); |
| } |
| |
| /** |
| * Called when we want to open bubblebar when user performs swipes up gesture. |
| */ |
| public void onSwipeToOpenBubblebar() { |
| mControllers.bubbleControllers.ifPresent(controllers -> { |
| controllers.bubbleStashController.showBubbleBar(/* expandBubbles= */ true); |
| }); |
| } |
| |
| /** Returns {@code true} if Taskbar All Apps is open. */ |
| public boolean isTaskbarAllAppsOpen() { |
| return mControllers.taskbarAllAppsController.isOpen(); |
| } |
| |
| /** Toggles the Taskbar's stash state. */ |
| public void toggleTaskbarStash() { |
| mControllers.taskbarStashController.toggleTaskbarStash(); |
| } |
| |
| /** |
| * Called to start the taskbar translation spring to its settled translation (0). |
| */ |
| public void startTranslationSpring() { |
| mControllers.taskbarTranslationController.startSpring(); |
| } |
| |
| /** |
| * Returns a callback to help monitor the swipe gesture. |
| */ |
| public TransitionCallback getTranslationCallbacks() { |
| return mControllers.taskbarTranslationController.getTransitionCallback(); |
| } |
| |
| /** |
| * Called when a transient Autohide flag suspend status changes. |
| */ |
| public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) { |
| mControllers.taskbarStashController.updateTaskbarTimeout(isSuspended); |
| } |
| |
| /** |
| * Called when we detect a motion down or up/cancel in the nav region while stashed. |
| * |
| * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. |
| */ |
| public void startTaskbarUnstashHint(boolean animateForward) { |
| // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. |
| startTaskbarUnstashHint(animateForward, /* forceUnstash = */ false); |
| } |
| |
| /** |
| * Called when we detect a motion down or up/cancel in the nav region while stashed. |
| * |
| * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. |
| * @param forceUnstash Whether we force the unstash hint. |
| */ |
| public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) { |
| // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. |
| mControllers.taskbarStashController.startUnstashHint(animateForward, forceUnstash); |
| } |
| |
| /** |
| * Enables manual taskbar stashing. This method should only be used for tests that need to |
| * stash/unstash the taskbar. |
| */ |
| @VisibleForTesting |
| public void enableManualStashingDuringTests(boolean enableManualStashing) { |
| mControllers.taskbarStashController.enableManualStashingDuringTests(enableManualStashing); |
| } |
| |
| /** |
| * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar |
| * testing. |
| */ |
| @VisibleForTesting |
| public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) { |
| mControllers.taskbarStashController.enableBlockingTimeoutDuringTests(enableBlockingTimeout); |
| } |
| |
| /** |
| * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the |
| * taskbar at the end of a test. |
| */ |
| @VisibleForTesting |
| public void unstashTaskbarIfStashed() { |
| if (DisplayController.isTransientTaskbar(this)) { |
| mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false); |
| } else { |
| mControllers.taskbarStashController.onLongPressToUnstashTaskbar(); |
| } |
| } |
| |
| protected boolean isUserSetupComplete() { |
| return mIsUserSetupComplete; |
| } |
| |
| public boolean isNavBarKidsModeActive() { |
| return mIsNavBarKidsMode && isThreeButtonNav(); |
| } |
| |
| protected boolean isNavBarForceVisible() { |
| return mIsNavBarForceVisible; |
| } |
| |
| /** |
| * Displays a single frame of the Launcher start from SUW animation. |
| * |
| * This animation is a combination of the Launcher resume animation, which animates the hotseat |
| * icons into position, the Taskbar unstash to hotseat animation, which animates the Taskbar |
| * stash bar into the hotseat icons, and an override to prevent showing the Taskbar all apps |
| * button. |
| * |
| * This should be used to run a Taskbar unstash to hotseat animation whose progress matches a |
| * swipe progress. |
| * |
| * @param duration a placeholder duration to be used to ensure all full-length |
| * sub-animations are properly coordinated. This duration should not actually |
| * be used since this animation tracks a swipe progress. |
| */ |
| protected AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) { |
| AnimatorSet fullAnimation = new AnimatorSet(); |
| fullAnimation.setDuration(duration); |
| |
| TaskbarUIController uiController = mControllers.uiController; |
| if (uiController instanceof LauncherTaskbarUIController) { |
| ((LauncherTaskbarUIController) uiController).addLauncherResumeAnimation( |
| fullAnimation, duration); |
| } |
| mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration); |
| |
| View allAppsButton = mControllers.taskbarViewController.getAllAppsButtonView(); |
| if (allAppsButton != null && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) { |
| ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1); |
| alphaOverride.setDuration(duration); |
| alphaOverride.addUpdateListener(a -> { |
| // Override the alpha updates in the icon alignment animation. |
| allAppsButton.setAlpha(0); |
| }); |
| fullAnimation.play(alphaOverride); |
| } |
| |
| return AnimatorPlaybackController.wrap(fullAnimation, duration); |
| } |
| |
| /** |
| * Called when we determine the touchable region. |
| * |
| * @param exclude {@code true} then the magnification region computation will omit the window. |
| */ |
| public void excludeFromMagnificationRegion(boolean exclude) { |
| if (mIsExcludeFromMagnificationRegion == exclude) { |
| return; |
| } |
| |
| mIsExcludeFromMagnificationRegion = exclude; |
| if (exclude) { |
| mWindowLayoutParams.privateFlags |= |
| WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; |
| } else { |
| mWindowLayoutParams.privateFlags &= |
| ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; |
| } |
| mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); |
| } |
| |
| void notifyUpdateLayoutParams() { |
| if (mDragLayer.isAttachedToWindow()) { |
| mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); |
| } |
| } |
| |
| public void showPopupMenuForIcon(BubbleTextView btv) { |
| setTaskbarWindowFullscreen(true); |
| btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv)); |
| } |
| |
| public boolean isInApp() { |
| return mControllers.taskbarStashController.isInApp(); |
| } |
| |
| public boolean isInStashedLauncherState() { |
| return mControllers.taskbarStashController.isInStashedLauncherState(); |
| } |
| |
| protected void dumpLogs(String prefix, PrintWriter pw) { |
| pw.println(prefix + "TaskbarActivityContext:"); |
| |
| pw.println(String.format( |
| "%s\tmNavMode=%s", prefix, mNavMode)); |
| pw.println(String.format( |
| "%s\tmImeDrawsImeNavBar=%b", prefix, mImeDrawsImeNavBar)); |
| pw.println(String.format( |
| "%s\tmIsUserSetupComplete=%b", prefix, mIsUserSetupComplete)); |
| pw.println(String.format( |
| "%s\tmWindowLayoutParams.height=%dpx", prefix, mWindowLayoutParams.height)); |
| pw.println(String.format( |
| "%s\tmBindInProgress=%b", prefix, mBindingItems)); |
| mControllers.dumpLogs(prefix + "\t", pw); |
| mDeviceProfile.dump(this, prefix, pw); |
| } |
| |
| @VisibleForTesting |
| public int getTaskbarAllAppsTopPadding() { |
| return mControllers.taskbarAllAppsController.getTaskbarAllAppsTopPadding(); |
| } |
| |
| @VisibleForTesting |
| public int getTaskbarAllAppsScroll() { |
| return mControllers.taskbarAllAppsController.getTaskbarAllAppsScroll(); |
| } |
| |
| @VisibleForTesting |
| public float getStashedTaskbarScale() { |
| return mControllers.stashedHandleViewController.getStashedHandleHintScale().value; |
| } |
| } |