f77c3cac2b
This cl adds the unpin option by tracking the hotseat/taskbar state. The option shown is determined by the following conditions: 1. If the target non-predicted item is on the taskbar, shows "Unpin from taskbar" 2. If the taskbar is not full, that is, reaching the limit of the available spaces, and the target item is anywhere outside of the taskbar, including All apps, shows "Pin to taskbar". 3. If the taskbar is full, simply don't show any shortcut option. This cl also removes the option that will be shown on Launcher homescreen or hotseat, as further UX alignment is needed. One note about why the pin shortcut is not implemented in the getShortcuts(). The reason is that getShortcuts does not have the ItemInfo of the triggered item, while the SystemShortcut.Factory doesn't have the hotseat/taskbar information. The simplest way at this point is to check all the conditions in the controller and then manually add the shortcut into the list. Bug: 375648361 Test: Manual, Recording uploaded to buganizer Flag: com.android.launcher3.enable_pinning_app_with_context_menu Change-Id: I7d048bcb1b00f78651e909fbfcd911052a4cd4ef
1513 lines
66 KiB
Java
1513 lines
66 KiB
Java
/*
|
|
* Copyright (C) 2022 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.uioverrides;
|
|
|
|
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
|
import static android.os.Trace.TRACE_TAG_APP;
|
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
|
|
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
|
|
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
|
|
|
|
import static com.android.app.animation.Interpolators.EMPHASIZED;
|
|
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
|
|
import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
|
|
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
|
|
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
|
|
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
|
|
import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
|
|
import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND;
|
|
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
|
import static com.android.launcher3.LauncherState.ALL_APPS;
|
|
import static com.android.launcher3.LauncherState.NORMAL;
|
|
import static com.android.launcher3.LauncherState.NO_OFFSET;
|
|
import static com.android.launcher3.LauncherState.OVERVIEW;
|
|
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
|
|
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
|
|
import static com.android.launcher3.Utilities.isRtl;
|
|
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED;
|
|
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
|
|
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
|
|
import static com.android.launcher3.popup.SystemShortcut.BUBBLE_SHORTCUT;
|
|
import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
|
|
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
|
|
import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
|
|
import static com.android.launcher3.popup.SystemShortcut.UNINSTALL_APP;
|
|
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
|
|
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX;
|
|
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX;
|
|
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.WIDGETS_PAGE_PROGRESS_INDEX;
|
|
import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL;
|
|
import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
|
|
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
|
|
import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
|
|
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
|
|
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
|
|
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
|
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
|
|
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
|
|
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.AnimatorSet;
|
|
import android.app.ActivityOptions;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentSender;
|
|
import android.content.pm.ShortcutInfo;
|
|
import android.content.res.Configuration;
|
|
import android.graphics.Rect;
|
|
import android.graphics.RectF;
|
|
import android.hardware.display.DisplayManager;
|
|
import android.media.permission.SafeCloseable;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.IRemoteCallback;
|
|
import android.os.SystemProperties;
|
|
import android.os.Trace;
|
|
import android.os.UserHandle;
|
|
import android.util.AttributeSet;
|
|
import android.view.Display;
|
|
import android.view.HapticFeedbackConstants;
|
|
import android.view.KeyEvent;
|
|
import android.view.View;
|
|
import android.widget.AnalogClock;
|
|
import android.widget.TextClock;
|
|
import android.window.BackEvent;
|
|
import android.window.OnBackAnimationCallback;
|
|
import android.window.OnBackInvokedDispatcher;
|
|
import android.window.RemoteTransition;
|
|
import android.window.SplashScreen;
|
|
|
|
import androidx.annotation.BinderThread;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RequiresApi;
|
|
|
|
import com.android.app.viewcapture.ViewCaptureFactory;
|
|
import com.android.launcher3.AbstractFloatingView;
|
|
import com.android.launcher3.DeviceProfile;
|
|
import com.android.launcher3.Flags;
|
|
import com.android.launcher3.InvariantDeviceProfile;
|
|
import com.android.launcher3.Launcher;
|
|
import com.android.launcher3.LauncherSettings.Favorites;
|
|
import com.android.launcher3.LauncherState;
|
|
import com.android.launcher3.QuickstepAccessibilityDelegate;
|
|
import com.android.launcher3.QuickstepTransitionManager;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.Utilities;
|
|
import com.android.launcher3.Workspace;
|
|
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
|
import com.android.launcher3.anim.AnimatorPlaybackController;
|
|
import com.android.launcher3.anim.PendingAnimation;
|
|
import com.android.launcher3.apppairs.AppPairIcon;
|
|
import com.android.launcher3.appprediction.PredictionRowView;
|
|
import com.android.launcher3.config.FeatureFlags;
|
|
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
|
|
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
|
|
import com.android.launcher3.logging.InstanceId;
|
|
import com.android.launcher3.logging.StatsLogManager;
|
|
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
|
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
|
import com.android.launcher3.model.WellbeingModel;
|
|
import com.android.launcher3.model.data.ItemInfo;
|
|
import com.android.launcher3.popup.SystemShortcut;
|
|
import com.android.launcher3.proxy.ProxyActivityStarter;
|
|
import com.android.launcher3.statehandlers.DepthController;
|
|
import com.android.launcher3.statehandlers.DesktopVisibilityController;
|
|
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
|
|
import com.android.launcher3.statemanager.StateManager.StateHandler;
|
|
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
|
|
import com.android.launcher3.taskbar.TaskbarManager;
|
|
import com.android.launcher3.taskbar.TaskbarUIController;
|
|
import com.android.launcher3.testing.TestLogging;
|
|
import com.android.launcher3.testing.shared.TestProtocol;
|
|
import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
|
|
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewDismissTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewLaunchTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
|
|
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
|
|
import com.android.launcher3.util.ActivityOptionsWrapper;
|
|
import com.android.launcher3.util.DisplayController;
|
|
import com.android.launcher3.util.IntSet;
|
|
import com.android.launcher3.util.NavigationMode;
|
|
import com.android.launcher3.util.ObjectWrapper;
|
|
import com.android.launcher3.util.PendingRequestArgs;
|
|
import com.android.launcher3.util.PendingSplitSelectInfo;
|
|
import com.android.launcher3.util.RunnableList;
|
|
import com.android.launcher3.util.SplitConfigurationOptions;
|
|
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
|
|
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
|
|
import com.android.launcher3.util.StartActivityParams;
|
|
import com.android.launcher3.util.TouchController;
|
|
import com.android.launcher3.widget.LauncherWidgetHolder;
|
|
import com.android.quickstep.OverviewCommandHelper;
|
|
import com.android.quickstep.OverviewComponentObserver;
|
|
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
|
|
import com.android.quickstep.RecentsModel;
|
|
import com.android.quickstep.SystemUiProxy;
|
|
import com.android.quickstep.TaskUtils;
|
|
import com.android.quickstep.TouchInteractionService.TISBinder;
|
|
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
|
|
import com.android.quickstep.util.AsyncClockEventDelegate;
|
|
import com.android.quickstep.util.LauncherUnfoldAnimationController;
|
|
import com.android.quickstep.util.QuickstepOnboardingPrefs;
|
|
import com.android.quickstep.util.SplitSelectStateController;
|
|
import com.android.quickstep.util.SplitTask;
|
|
import com.android.quickstep.util.SplitToWorkspaceController;
|
|
import com.android.quickstep.util.SplitWithKeyboardShortcutController;
|
|
import com.android.quickstep.util.TISBindHelper;
|
|
import com.android.quickstep.util.unfold.LauncherUnfoldTransitionController;
|
|
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
|
|
import com.android.quickstep.views.FloatingTaskView;
|
|
import com.android.quickstep.views.OverviewActionsView;
|
|
import com.android.quickstep.views.RecentsView;
|
|
import com.android.quickstep.views.RecentsViewContainer;
|
|
import com.android.quickstep.views.TaskView;
|
|
import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
|
|
import com.android.systemui.shared.recents.model.Task;
|
|
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
|
import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
|
|
import com.android.systemui.unfold.UnfoldTransitionFactory;
|
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
|
|
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
|
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
|
|
import com.android.systemui.unfold.dagger.UnfoldMain;
|
|
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
|
|
import com.android.systemui.unfold.updates.RotationChangeProvider;
|
|
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
|
|
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
|
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
|
|
|
|
import kotlin.Unit;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.function.BiConsumer;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Stream;
|
|
|
|
public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
|
SystemShortcut.BubbleActivityStarter {
|
|
private static final boolean TRACE_LAYOUTS =
|
|
SystemProperties.getBoolean("persist.debug.trace_layouts", false);
|
|
private static final String TRACE_RELAYOUT_CLASS =
|
|
SystemProperties.get("persist.debug.trace_request_layout_class", null);
|
|
|
|
protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t";
|
|
|
|
private FixedContainerItems mAllAppsPredictions;
|
|
private HotseatPredictionController mHotseatPredictionController;
|
|
private DepthController mDepthController;
|
|
private QuickstepTransitionManager mAppTransitionManager;
|
|
|
|
private OverviewActionsView<?> mActionsView;
|
|
private TISBindHelper mTISBindHelper;
|
|
private @Nullable LauncherTaskbarUIController mTaskbarUIController;
|
|
// Will be updated when dragging from taskbar.
|
|
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
|
|
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
|
|
|
|
private SplitSelectStateController mSplitSelectStateController;
|
|
private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
|
|
private SplitToWorkspaceController mSplitToWorkspaceController;
|
|
private BubbleBarLocation mBubbleBarLocation;
|
|
|
|
/**
|
|
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
|
|
* recover. In all other cases this will remain null.
|
|
*/
|
|
private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
|
|
|
|
@Nullable
|
|
private DesktopRecentsTransitionController mDesktopRecentsTransitionController;
|
|
|
|
private SafeCloseable mViewCapture;
|
|
|
|
private boolean mEnableWidgetDepth;
|
|
|
|
private boolean mIsPredictiveBackToHomeInProgress;
|
|
|
|
private boolean mCanShowAllAppsEducationView;
|
|
|
|
private boolean mIsOverlayVisible;
|
|
|
|
private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
|
|
|
|
private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext =
|
|
new TaskViewRecentsTouchContext() {
|
|
@Override
|
|
public boolean isRecentsInteractive() {
|
|
return isInState(OVERVIEW) || isInState(OVERVIEW_MODAL_TASK);
|
|
}
|
|
|
|
@Override
|
|
public boolean isRecentsModal() {
|
|
return isInState(OVERVIEW_MODAL_TASK);
|
|
}
|
|
|
|
@Override
|
|
public void onUserControlledAnimationCreated(
|
|
AnimatorPlaybackController animController) {
|
|
getStateManager().setCurrentUserControlledAnimation(animController);
|
|
}
|
|
};
|
|
|
|
public static QuickstepLauncher getLauncher(Context context) {
|
|
return fromContext(context);
|
|
}
|
|
|
|
@Override
|
|
protected void setupViews() {
|
|
super.setupViews();
|
|
|
|
mActionsView = findViewById(R.id.overview_actions_view);
|
|
RecentsView<?,?> overviewPanel = getOverviewPanel();
|
|
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
|
|
mSplitSelectStateController =
|
|
new SplitSelectStateController(this, getStateManager(),
|
|
getDepthController(), getStatsLogManager(),
|
|
systemUiProxy, RecentsModel.INSTANCE.get(this),
|
|
() -> onStateBack());
|
|
if (DesktopModeStatus.canEnterDesktopMode(this)) {
|
|
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
|
|
getStateManager(), systemUiProxy, getIApplicationThread(),
|
|
getDepthController());
|
|
}
|
|
overviewPanel.init(mActionsView, mSplitSelectStateController,
|
|
mDesktopRecentsTransitionController);
|
|
mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(
|
|
this, mSplitSelectStateController);
|
|
mSplitToWorkspaceController = new SplitToWorkspaceController(this,
|
|
mSplitSelectStateController);
|
|
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
|
|
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
|
|
|
|
mAppTransitionManager = buildAppTransitionManager();
|
|
mAppTransitionManager.registerRemoteAnimations();
|
|
mAppTransitionManager.registerRemoteTransitions();
|
|
|
|
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
|
|
mDepthController = new DepthController(this);
|
|
if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(this)) {
|
|
mSplitSelectStateController.initSplitFromDesktopController(this);
|
|
}
|
|
mHotseatPredictionController = new HotseatPredictionController(this);
|
|
|
|
mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true);
|
|
getWorkspace().addOverlayCallback(progress ->
|
|
onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
|
|
addBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
|
|
}
|
|
|
|
@Override
|
|
public void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
|
|
InstanceId instanceId) {
|
|
// If the app launch is from any of the surfaces in AllApps then add the InstanceId from
|
|
// LiveSearchManager to recreate the AllApps session on the server side.
|
|
if (mAllAppsSessionLogId != null && ALL_APPS.equals(
|
|
getStateManager().getCurrentStableState())) {
|
|
instanceId = mAllAppsSessionLogId;
|
|
}
|
|
|
|
StatsLogger logger = statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId);
|
|
|
|
if (mAllAppsPredictions != null
|
|
&& (info.itemType == ITEM_TYPE_APPLICATION
|
|
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
|
|
int count = mAllAppsPredictions.items.size();
|
|
for (int i = 0; i < count; i++) {
|
|
ItemInfo targetInfo = mAllAppsPredictions.items.get(i);
|
|
if (targetInfo.itemType == info.itemType
|
|
&& targetInfo.user.equals(info.user)
|
|
&& Objects.equals(targetInfo.getIntent(), info.getIntent())) {
|
|
logger.withRank(i);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
logger.log(LAUNCHER_APP_LAUNCH_TAP);
|
|
|
|
mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
|
|
}
|
|
|
|
@Override
|
|
protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
|
|
int cellY, PendingRequestArgs args) {
|
|
if (container == CONTAINER_HOTSEAT) {
|
|
mHotseatPredictionController.onDeferredDrop(cellX, cellY);
|
|
}
|
|
super.completeAddShortcut(data, container, screenId, cellX, cellY, args);
|
|
}
|
|
|
|
@Override
|
|
protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
|
|
return new QuickstepAccessibilityDelegate(this);
|
|
}
|
|
|
|
/**
|
|
* Returns Prediction controller for hybrid hotseat
|
|
*/
|
|
public HotseatPredictionController getHotseatPredictionController() {
|
|
return mHotseatPredictionController;
|
|
}
|
|
|
|
@Override
|
|
public void enableHotseatEdu(boolean enable) {
|
|
super.enableHotseatEdu(enable);
|
|
mHotseatPredictionController.enableHotseatEdu(enable);
|
|
}
|
|
|
|
/**
|
|
* Builds the {@link QuickstepTransitionManager} instance to use for managing transitions.
|
|
*/
|
|
protected QuickstepTransitionManager buildAppTransitionManager() {
|
|
return new QuickstepTransitionManager(this);
|
|
}
|
|
|
|
@Override
|
|
public void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
onStateOrResumeChanging(false /* inTransition */);
|
|
}
|
|
|
|
@Override
|
|
public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
|
|
// Only pause is taskbar controller is not present until the transition (if it exists) ends
|
|
mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
|
|
PredictionRowView<?> predictionRowView =
|
|
getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class);
|
|
// Pause the prediction row updates until the transition (if it exists) ends.
|
|
predictionRowView.setPredictionUiUpdatePaused(true);
|
|
RunnableList result = super.startActivitySafely(v, intent, item);
|
|
if (result == null) {
|
|
mHotseatPredictionController.setPauseUIUpdate(false);
|
|
predictionRowView.setPredictionUiUpdatePaused(false);
|
|
} else {
|
|
result.add(() -> {
|
|
mHotseatPredictionController.setPauseUIUpdate(false);
|
|
predictionRowView.setPredictionUiUpdatePaused(false);
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void startBinding() {
|
|
super.startBinding();
|
|
mHotseatPredictionController.verifyUIUpdateNotPaused();
|
|
}
|
|
|
|
@Override
|
|
protected void onActivityFlagsChanged(int changeBits) {
|
|
if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
|
|
mDepthController.setActivityStarted(isStarted());
|
|
}
|
|
|
|
if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
|
|
if (!FeatureFlags.enableHomeTransitionListener() && mTaskbarUIController != null) {
|
|
mTaskbarUIController.onLauncherVisibilityChanged(hasBeenResumed());
|
|
}
|
|
}
|
|
|
|
super.onActivityFlagsChanged(changeBits);
|
|
if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
|
|
| ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
|
|
onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void showAllAppsFromIntent(boolean alreadyOnHome) {
|
|
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
|
|
super.showAllAppsFromIntent(alreadyOnHome);
|
|
}
|
|
|
|
protected void onItemClicked(View view) {
|
|
if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) {
|
|
super.getItemOnClickListener().onClick(view);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public View.OnClickListener getItemOnClickListener() {
|
|
return this::onItemClicked;
|
|
}
|
|
|
|
@Override
|
|
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
|
|
// Order matters as it affects order of appearance in popup container
|
|
List<SystemShortcut.Factory> shortcuts = new ArrayList(Arrays.asList(
|
|
APP_INFO, WellbeingModel.SHORTCUT_FACTORY, mHotseatPredictionController));
|
|
|
|
shortcuts.addAll(getSplitShortcuts());
|
|
shortcuts.add(WIDGETS);
|
|
shortcuts.add(INSTALL);
|
|
if (Flags.enablePrivateSpaceInstallShortcut()) {
|
|
shortcuts.add(PRIVATE_PROFILE_INSTALL);
|
|
}
|
|
if (Flags.enableShortcutDontSuggestApp()) {
|
|
shortcuts.add(DONT_SUGGEST_APP);
|
|
}
|
|
if (Flags.enablePrivateSpace()) {
|
|
shortcuts.add(UNINSTALL_APP);
|
|
}
|
|
if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
|
|
shortcuts.add(BUBBLE_SHORTCUT);
|
|
}
|
|
return shortcuts.stream();
|
|
}
|
|
|
|
private List<SystemShortcut.Factory<QuickstepLauncher>> getSplitShortcuts() {
|
|
if (!mDeviceProfile.isTablet || mSplitSelectStateController.isSplitSelectActive()) {
|
|
return Collections.emptyList();
|
|
}
|
|
RecentsView recentsView = getOverviewPanel();
|
|
// TODO(b/266482558): Pull it out of PagedOrentationHandler for split from workspace.
|
|
List<SplitPositionOption> positions =
|
|
recentsView.getPagedOrientationHandler().getSplitPositionOptions(
|
|
mDeviceProfile);
|
|
List<SystemShortcut.Factory<QuickstepLauncher>> splitShortcuts = new ArrayList<>();
|
|
for (SplitPositionOption position : positions) {
|
|
splitShortcuts.add(getSplitSelectShortcutByPosition(position));
|
|
}
|
|
return splitShortcuts;
|
|
}
|
|
|
|
/**
|
|
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
|
|
*/
|
|
private void onStateOrResumeChanging(boolean inTransition) {
|
|
LauncherState state = getStateManager().getState();
|
|
boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
|
|
if (started) {
|
|
DeviceProfile profile = getDeviceProfile();
|
|
boolean visible = (state == NORMAL || state == OVERVIEW)
|
|
&& isUserActive()
|
|
&& !profile.isVerticalBarLayout()
|
|
&& !mIsOverlayVisible;
|
|
SystemUiProxy.INSTANCE.get(this)
|
|
.setLauncherKeepClearAreaHeight(visible, profile.hotseatBarSizePx);
|
|
}
|
|
if (state == NORMAL && !inTransition) {
|
|
((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onOverlayVisibilityChanged(boolean visible) {
|
|
super.onOverlayVisibilityChanged(visible);
|
|
mIsOverlayVisible = visible;
|
|
}
|
|
|
|
@Override
|
|
public void bindExtraContainerItems(FixedContainerItems item) {
|
|
if (item.containerId == Favorites.CONTAINER_PREDICTION) {
|
|
mAllAppsPredictions = item;
|
|
PredictionRowView<?> predictionRowView =
|
|
getAppsView().getFloatingHeaderView().findFixedRowByType(
|
|
PredictionRowView.class);
|
|
predictionRowView.setPredictedApps(item.items);
|
|
} else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
|
|
mHotseatPredictionController.setPredictedItems(item);
|
|
} else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
|
|
getWidgetPickerDataProvider().setWidgetRecommendations(item.items);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) {
|
|
super.bindWorkspaceComponentsRemoved(matcher);
|
|
mHotseatPredictionController.onModelItemsRemoved(matcher);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
if (mAppTransitionManager != null) {
|
|
mAppTransitionManager.onActivityDestroyed();
|
|
}
|
|
mAppTransitionManager = null;
|
|
mIsPredictiveBackToHomeInProgress = false;
|
|
|
|
if (mUnfoldTransitionProgressProvider != null) {
|
|
SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
|
|
mUnfoldTransitionProgressProvider.destroy();
|
|
}
|
|
|
|
OverviewComponentObserver.INSTANCE.get(this)
|
|
.removeOverviewChangeListener(mOverviewChangeListener);
|
|
mTISBindHelper.onDestroy();
|
|
|
|
if (mLauncherUnfoldAnimationController != null) {
|
|
mLauncherUnfoldAnimationController.onDestroy();
|
|
}
|
|
|
|
if (mSplitSelectStateController != null) {
|
|
mSplitSelectStateController.onDestroy();
|
|
}
|
|
|
|
RecentsView recentsView = getOverviewPanel();
|
|
if (recentsView != null) {
|
|
recentsView.destroy();
|
|
}
|
|
|
|
super.onDestroy();
|
|
mHotseatPredictionController.destroy();
|
|
if (mViewCapture != null) mViewCapture.close();
|
|
removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
|
|
}
|
|
|
|
@Override
|
|
public void onStateSetEnd(LauncherState state) {
|
|
super.onStateSetEnd(state);
|
|
handlePendingActivityRequest();
|
|
|
|
switch (state.ordinal) {
|
|
case HINT_STATE_ORDINAL: {
|
|
Workspace<?> workspace = getWorkspace();
|
|
getStateManager().goToState(NORMAL);
|
|
if (workspace.getNextPage() != Workspace.DEFAULT_PAGE) {
|
|
workspace.post(workspace::moveToDefaultScreen);
|
|
}
|
|
break;
|
|
}
|
|
case HINT_STATE_TWO_BUTTON_ORDINAL: {
|
|
getStateManager().goToState(OVERVIEW);
|
|
getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
|
|
break;
|
|
}
|
|
case OVERVIEW_STATE_ORDINAL: {
|
|
RecentsView rv = getOverviewPanel();
|
|
sendCustomAccessibilityEvent(
|
|
rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
|
|
break;
|
|
}
|
|
case QUICK_SWITCH_STATE_ORDINAL: {
|
|
RecentsView rv = getOverviewPanel();
|
|
TaskView currentPageTask = rv.getCurrentPageTaskView();
|
|
TaskView fallbackTask = rv.getFirstTaskView();
|
|
if (currentPageTask != null || fallbackTask != null) {
|
|
TaskView taskToLaunch = currentPageTask;
|
|
if (currentPageTask == null) {
|
|
taskToLaunch = fallbackTask;
|
|
ActiveGestureProtoLogProxy.logQuickSwitchFromHomeFallback(
|
|
rv.getCurrentPage());
|
|
}
|
|
taskToLaunch.launchWithoutAnimation(success -> {
|
|
if (!success) {
|
|
getStateManager().goToState(OVERVIEW);
|
|
} else {
|
|
getStateManager().moveToRestState();
|
|
}
|
|
return Unit.INSTANCE;
|
|
});
|
|
} else {
|
|
ActiveGestureProtoLogProxy.logQuickSwitchFromHomeFailed(rv.getCurrentPage());
|
|
getStateManager().goToState(NORMAL);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public TouchController[] createTouchControllers() {
|
|
NavigationMode mode = DisplayController.getNavigationMode(this);
|
|
|
|
ArrayList<TouchController> list = new ArrayList<>();
|
|
list.add(getDragController());
|
|
BiConsumer<AnimatorSet, Long> splitAnimator = (animatorSet, duration) ->
|
|
animatorSet.play(mSplitSelectStateController.getSplitAnimationController()
|
|
.createPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
|
|
duration));
|
|
switch (mode) {
|
|
case NO_BUTTON:
|
|
list.add(new NoButtonQuickSwitchTouchController(this));
|
|
list.add(new NavBarToHomeTouchController(this, splitAnimator));
|
|
list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator));
|
|
break;
|
|
case TWO_BUTTONS:
|
|
list.add(new TwoButtonNavbarTouchController(this));
|
|
list.add(getDeviceProfile().isVerticalBarLayout()
|
|
? new TransposedQuickSwitchTouchController(this)
|
|
: new QuickSwitchTouchController(this));
|
|
list.add(new PortraitStatesTouchController(this));
|
|
break;
|
|
case THREE_BUTTONS:
|
|
list.add(new NoButtonQuickSwitchTouchController(this));
|
|
list.add(new NavBarToHomeTouchController(this, splitAnimator));
|
|
list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator));
|
|
list.add(new PortraitStatesTouchController(this));
|
|
break;
|
|
default:
|
|
list.add(new PortraitStatesTouchController(this));
|
|
break;
|
|
}
|
|
|
|
if (!getDeviceProfile().isMultiWindowMode) {
|
|
list.add(new StatusBarTouchController(this));
|
|
}
|
|
|
|
if (enableExpressiveDismissTaskMotion()) {
|
|
list.add(new TaskViewLaunchTouchController<>(this, mTaskViewRecentsTouchContext));
|
|
list.add(new TaskViewDismissTouchController<>(this, mTaskViewRecentsTouchContext));
|
|
} else {
|
|
list.add(new TaskViewTouchControllerDeprecated<>(this, mTaskViewRecentsTouchContext));
|
|
}
|
|
return list.toArray(new TouchController[list.size()]);
|
|
}
|
|
|
|
@Override
|
|
public AtomicAnimationFactory createAtomicAnimationFactory() {
|
|
return new QuickstepAtomicAnimationFactory(this);
|
|
}
|
|
|
|
@Override
|
|
protected LauncherWidgetHolder createAppWidgetHolder() {
|
|
final QuickstepHolderFactory factory =
|
|
(QuickstepHolderFactory) LauncherWidgetHolder.HolderFactory.newFactory(this);
|
|
return factory.newInstance(this,
|
|
appWidgetId -> getWorkspace().removeWidget(appWidgetId),
|
|
new QuickstepInteractionHandler(this));
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
if (savedInstanceState != null) {
|
|
mPendingSplitSelectInfo = ObjectWrapper.unwrap(
|
|
savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO));
|
|
}
|
|
addMultiWindowModeChangedListener(mDepthController);
|
|
initUnfoldTransitionProgressProvider();
|
|
mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
|
|
getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE);
|
|
QuickstepOnboardingPrefs.setup(this);
|
|
View.setTraceLayoutSteps(TRACE_LAYOUTS);
|
|
View.setTracedRequestLayoutClassClass(TRACE_RELAYOUT_CLASS);
|
|
OverviewComponentObserver.INSTANCE.get(this)
|
|
.addOverviewChangeListener(mOverviewChangeListener);
|
|
}
|
|
|
|
@Override
|
|
protected boolean initDeviceProfile(InvariantDeviceProfile idp) {
|
|
final boolean ret = super.initDeviceProfile(idp);
|
|
mDeviceProfile.isPredictiveBackSwipe =
|
|
getApplicationInfo().isOnBackInvokedCallbackEnabled();
|
|
if (ret) {
|
|
SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
@Override
|
|
public void startSplitSelection(SplitSelectSource splitSelectSource) {
|
|
RecentsView recentsView = getOverviewPanel();
|
|
// Check if there is already an instance of this app running, if so, initiate the split
|
|
// using that.
|
|
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
|
|
Collections.singletonList(splitSelectSource.getItemInfo().getComponentKey()),
|
|
false /* findExactPairMatch */,
|
|
foundTasks -> {
|
|
@Nullable Task foundTask = foundTasks[0];
|
|
boolean taskWasFound = foundTask != null;
|
|
splitSelectSource.alreadyRunningTaskId = taskWasFound
|
|
? foundTask.key.id
|
|
: INVALID_TASK_ID;
|
|
startSplitToHome(splitSelectSource);
|
|
}
|
|
);
|
|
}
|
|
|
|
/** TODO(b/266482558) Migrate into SplitSelectStateController or someplace split specific. */
|
|
private void startSplitToHome(SplitSelectSource source) {
|
|
AbstractFloatingView.closeAllOpenViews(this);
|
|
int splitPlaceholderSize = getResources().getDimensionPixelSize(
|
|
R.dimen.split_placeholder_size);
|
|
int splitPlaceholderInset = getResources().getDimensionPixelSize(
|
|
R.dimen.split_placeholder_inset);
|
|
Rect tempRect = new Rect();
|
|
|
|
mSplitSelectStateController.setInitialTaskSelect(source.intent,
|
|
source.position.stagePosition, source.getItemInfo(), source.splitEvent,
|
|
source.alreadyRunningTaskId);
|
|
|
|
RecentsView recentsView = getOverviewPanel();
|
|
recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
|
|
splitPlaceholderSize, splitPlaceholderInset, getDeviceProfile(),
|
|
mSplitSelectStateController.getActiveSplitStagePosition(), tempRect);
|
|
|
|
PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
|
|
RectF startingTaskRect = new RectF();
|
|
final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(this,
|
|
source.getView(), null /* thumbnail */, source.getDrawable(), startingTaskRect);
|
|
floatingTaskView.setAlpha(1);
|
|
floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect,
|
|
false /* fadeWithThumbnail */, true /* isStagedTask */);
|
|
floatingTaskView.setOnClickListener(view ->
|
|
mSplitSelectStateController.getSplitAnimationController().
|
|
playAnimPlaceholderToFullscreen(this, view, Optional.empty()));
|
|
floatingTaskView.setContentDescription(source.getItemInfo().contentDescription);
|
|
|
|
mSplitSelectStateController.setFirstFloatingTaskView(floatingTaskView);
|
|
anim.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationCancel(Animator animation) {
|
|
getDragLayer().removeView(floatingTaskView);
|
|
mSplitSelectStateController.getSplitAnimationController()
|
|
.removeSplitInstructionsView(QuickstepLauncher.this);
|
|
mSplitSelectStateController.resetState();
|
|
}
|
|
});
|
|
anim.add(mSplitSelectStateController.getSplitAnimationController()
|
|
.getShowSplitInstructionsAnim(this).buildAnim());
|
|
anim.buildAnim().start();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSplitSelectionActive() {
|
|
if (mSplitSelectStateController == null) {
|
|
return false;
|
|
}
|
|
return mSplitSelectStateController.isSplitSelectActive();
|
|
}
|
|
|
|
public boolean areBothSplitAppsConfirmed() {
|
|
return mSplitSelectStateController.isBothSplitAppsConfirmed();
|
|
}
|
|
|
|
@Override
|
|
public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) {
|
|
if (mTaskbarUIController != null) {
|
|
mTaskbarUIController.onStateTransitionCompletedAfterSwipeToHome(finalState);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
|
|
if (mLauncherUnfoldAnimationController != null) {
|
|
mLauncherUnfoldAnimationController.onResume();
|
|
}
|
|
|
|
if (mTaskbarUIController != null && FeatureFlags.enableHomeTransitionListener()) {
|
|
mTaskbarUIController.onLauncherResume();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
if (mLauncherUnfoldAnimationController != null) {
|
|
mLauncherUnfoldAnimationController.onPause();
|
|
}
|
|
|
|
super.onPause();
|
|
|
|
// If Launcher pauses before both split apps are selected, exit split screen.
|
|
if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
|
|
!mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
|
|
mSplitSelectStateController
|
|
.logExitReason(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
|
|
mSplitSelectStateController.getSplitAnimationController()
|
|
.playPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED);
|
|
}
|
|
|
|
if (mTaskbarUIController != null && FeatureFlags.enableHomeTransitionListener()) {
|
|
mTaskbarUIController.onLauncherPause();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStop() {
|
|
super.onStop();
|
|
if (mTaskbarUIController != null && FeatureFlags.enableHomeTransitionListener()) {
|
|
mTaskbarUIController.onLauncherStop();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onNewIntent(Intent intent) {
|
|
super.onNewIntent(intent);
|
|
OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
|
|
if (overviewCommandHelper != null) {
|
|
overviewCommandHelper.clearPendingCommands();
|
|
}
|
|
}
|
|
|
|
public QuickstepTransitionManager getAppTransitionManager() {
|
|
return mAppTransitionManager;
|
|
}
|
|
|
|
@Override
|
|
public void onEnterAnimationComplete() {
|
|
super.onEnterAnimationComplete();
|
|
// After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
|
|
// as a part of quickstep, so that high-res thumbnails can load the next time we enter
|
|
// overview
|
|
RecentsModel.INSTANCE.get(this).getThumbnailCache()
|
|
.getHighResLoadingState().setVisible(true);
|
|
}
|
|
|
|
@Override
|
|
protected void handleGestureContract(Intent intent) {
|
|
if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
|
|
super.handleGestureContract(intent);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onTrimMemory(int level) {
|
|
super.onTrimMemory(level);
|
|
RecentsModel.INSTANCE.get(this).onTrimMemory(level);
|
|
}
|
|
|
|
@Override
|
|
public void onUiChangedWhileSleeping() {
|
|
// Remove the snapshot because the content view may have obvious changes.
|
|
UI_HELPER_EXECUTOR.execute(
|
|
() -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
|
|
}
|
|
|
|
@Override
|
|
public void onAllAppsTransition(float progress) {
|
|
super.onAllAppsTransition(progress);
|
|
onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX);
|
|
}
|
|
|
|
@Override
|
|
public void onWidgetsTransition(float progress) {
|
|
super.onWidgetsTransition(progress);
|
|
onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
|
|
if (mEnableWidgetDepth) {
|
|
getDepthController().widgetDepth.setValue(Utilities.mapToRange(
|
|
progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
return tryHandleBackKey(event) || super.dispatchKeyEvent(event);
|
|
}
|
|
|
|
// TODO (b/267248420) Once the recents input consumer has been removed, there is no need to
|
|
// handle the back key specially.
|
|
private boolean tryHandleBackKey(KeyEvent event) {
|
|
// Unlike normal activity, recents can receive input event from InputConsumer, so the input
|
|
// event won't go through ViewRootImpl#InputStage#onProcess.
|
|
// So when receive back key, try to do the same check thing in
|
|
// ViewRootImpl#NativePreImeInputStage#onProcess
|
|
if (event.getKeyCode() != KeyEvent.KEYCODE_BACK
|
|
|| event.getAction() != KeyEvent.ACTION_UP || event.isCanceled()) {
|
|
return false;
|
|
}
|
|
|
|
getOnBackAnimationCallback().onBackInvoked();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void registerBackDispatcher() {
|
|
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
|
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
|
new FlingOnBackAnimationCallback() {
|
|
|
|
@Nullable OnBackAnimationCallback mActiveOnBackAnimationCallback;
|
|
|
|
@Override
|
|
public void onBackStartedCompat(@NonNull BackEvent backEvent) {
|
|
if (mActiveOnBackAnimationCallback != null) {
|
|
mActiveOnBackAnimationCallback.onBackCancelled();
|
|
}
|
|
mActiveOnBackAnimationCallback = getOnBackAnimationCallback();
|
|
mActiveOnBackAnimationCallback.onBackStarted(backEvent);
|
|
}
|
|
|
|
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
@Override
|
|
public void onBackInvokedCompat() {
|
|
// Recreate mActiveOnBackAnimationCallback if necessary to avoid NPE
|
|
// because:
|
|
// 1. b/260636433: In 3-button-navigation mode, onBackStarted() is not
|
|
// called on ACTION_DOWN before onBackInvoked() is called in ACTION_UP.
|
|
// 2. Launcher#onBackPressed() will call onBackInvoked() without calling
|
|
// onBackInvoked() beforehand.
|
|
if (mActiveOnBackAnimationCallback == null) {
|
|
mActiveOnBackAnimationCallback = getOnBackAnimationCallback();
|
|
}
|
|
mActiveOnBackAnimationCallback.onBackInvoked();
|
|
mActiveOnBackAnimationCallback = null;
|
|
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
|
|
}
|
|
|
|
@Override
|
|
public void onBackProgressedCompat(@NonNull BackEvent backEvent) {
|
|
if (!FeatureFlags.IS_STUDIO_BUILD
|
|
&& mActiveOnBackAnimationCallback == null) {
|
|
return;
|
|
}
|
|
mActiveOnBackAnimationCallback.onBackProgressed(backEvent);
|
|
}
|
|
|
|
@Override
|
|
public void onBackCancelledCompat() {
|
|
if (!FeatureFlags.IS_STUDIO_BUILD
|
|
&& mActiveOnBackAnimationCallback == null) {
|
|
return;
|
|
}
|
|
mActiveOnBackAnimationCallback.onBackCancelled();
|
|
mActiveOnBackAnimationCallback = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
|
|
TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
|
|
if (taskbarManager == null
|
|
|| taskbarManager.getCurrentActivityContext() == null
|
|
|| mTaskbarUIController == null) {
|
|
return;
|
|
}
|
|
mTaskbarUIController.onTaskbarInAppDisplayProgressUpdate(progress, flag);
|
|
}
|
|
|
|
@Override
|
|
public void startIntentSenderForResult(IntentSender intent, int requestCode,
|
|
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
|
|
if (requestCode != -1) {
|
|
mPendingActivityRequestCode = requestCode;
|
|
StartActivityParams params = new StartActivityParams(this, requestCode);
|
|
params.intentSender = intent;
|
|
params.fillInIntent = fillInIntent;
|
|
params.flagsMask = flagsMask;
|
|
params.flagsValues = flagsValues;
|
|
params.extraFlags = extraFlags;
|
|
params.options = options;
|
|
startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
|
|
} else {
|
|
super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
|
|
flagsValues, extraFlags, options);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
|
if (requestCode != -1) {
|
|
mPendingActivityRequestCode = requestCode;
|
|
StartActivityParams params = new StartActivityParams(this, requestCode);
|
|
params.intent = intent;
|
|
params.options = options;
|
|
startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
|
|
} else {
|
|
super.startActivityForResult(intent, requestCode, options);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setResumed() {
|
|
DesktopVisibilityController desktopVisibilityController =
|
|
DesktopVisibilityController.INSTANCE.get(this);
|
|
if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
|
|
&& desktopVisibilityController.isInDesktopModeAndNotInOverview(getDisplayId())
|
|
&& !desktopVisibilityController.isRecentsGestureInProgress()) {
|
|
// Return early to skip setting activity to appear as resumed
|
|
// TODO: b/333533253 - Remove after flag rollout
|
|
return;
|
|
}
|
|
super.setResumed();
|
|
}
|
|
|
|
@Override
|
|
protected void onDeferredResumed() {
|
|
super.onDeferredResumed();
|
|
handlePendingActivityRequest();
|
|
}
|
|
|
|
private void handlePendingActivityRequest() {
|
|
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)
|
|
&& ((getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
|
|
// Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
|
|
onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
|
|
// ProxyActivityStarter is started with clear task to reset the task after which it
|
|
// removes the task itself.
|
|
startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
|
|
}
|
|
}
|
|
|
|
private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
|
|
QuickstepTransitionManager transitionManager = getAppTransitionManager();
|
|
if (transitionManager != null) {
|
|
transitionManager.onOverviewTargetChange();
|
|
}
|
|
}
|
|
|
|
private void onTISConnected(TISBinder binder) {
|
|
TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
|
|
if (taskbarManager != null) {
|
|
taskbarManager.setActivity(this);
|
|
}
|
|
mTISBindHelper.setPredictiveBackToHomeInProgress(mIsPredictiveBackToHomeInProgress);
|
|
}
|
|
|
|
@Override
|
|
public void runOnBindToTouchInteractionService(Runnable r) {
|
|
mTISBindHelper.runOnBindToTouchInteractionService(r);
|
|
}
|
|
|
|
private void initUnfoldTransitionProgressProvider() {
|
|
if (!enableUnfoldStateAnimation()) {
|
|
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
|
|
if (config.isEnabled()) {
|
|
initRemotelyCalculatedUnfoldAnimation(config);
|
|
}
|
|
} else {
|
|
ProxyUnfoldTransitionProvider provider =
|
|
SystemUiProxy.INSTANCE.get(this).getUnfoldTransitionProvider();
|
|
if (provider != null) {
|
|
new LauncherUnfoldTransitionController(this, provider);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Receives animation progress from sysui process. */
|
|
private void initRemotelyCalculatedUnfoldAnimation(UnfoldTransitionConfig config) {
|
|
RemoteUnfoldSharedComponent unfoldComponent =
|
|
UnfoldTransitionFactory.createRemoteUnfoldSharedComponent(
|
|
/* context= */ this,
|
|
config,
|
|
getMainExecutor(),
|
|
getMainThreadHandler(),
|
|
/* backgroundExecutor= */ UI_HELPER_EXECUTOR,
|
|
/* bgHandler= */ UI_HELPER_EXECUTOR.getHandler(),
|
|
/* tracingTagPrefix= */ "launcher",
|
|
getSystemService(DisplayManager.class)
|
|
);
|
|
|
|
final RemoteUnfoldTransitionReceiver remoteUnfoldTransitionProgressProvider =
|
|
unfoldComponent.getRemoteTransitionProgress().orElseThrow(
|
|
() -> new IllegalStateException(
|
|
"Trying to create getRemoteTransitionProgress when the transition "
|
|
+ "is disabled"));
|
|
mUnfoldTransitionProgressProvider = remoteUnfoldTransitionProgressProvider;
|
|
|
|
SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(
|
|
remoteUnfoldTransitionProgressProvider);
|
|
|
|
initUnfoldAnimationController(mUnfoldTransitionProgressProvider,
|
|
unfoldComponent.getRotationChangeProvider());
|
|
}
|
|
|
|
private void initUnfoldAnimationController(UnfoldTransitionProgressProvider progressProvider,
|
|
@UnfoldMain RotationChangeProvider rotationChangeProvider) {
|
|
mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
|
|
/* launcher= */ this,
|
|
getWindowManager(),
|
|
progressProvider,
|
|
rotationChangeProvider
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public void setTaskbarUIController(@Nullable TaskbarUIController taskbarUIController) {
|
|
mTaskbarUIController = (LauncherTaskbarUIController) taskbarUIController;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
|
|
return mTaskbarUIController;
|
|
}
|
|
|
|
/** Provides the translation X for the hotseat item. */
|
|
public int getHotseatItemTranslationX(ItemInfo itemInfo) {
|
|
int translationX = 0;
|
|
if (isBubbleBarEnabled() && mBubbleBarLocation != null) {
|
|
boolean isBubblesOnLeft = mBubbleBarLocation.isOnLeft(isRtl(getResources()));
|
|
translationX += mDeviceProfile
|
|
.getHotseatTranslationXForNavBar(this, isBubblesOnLeft);
|
|
}
|
|
if (isBubbleBarEnabled()
|
|
&& mDeviceProfile.shouldAdjustHotseatForBubbleBar(asContext(), hasBubbles())) {
|
|
translationX += (int) mDeviceProfile
|
|
.getHotseatAdjustedTranslation(asContext(), itemInfo.cellX);
|
|
}
|
|
return translationX;
|
|
}
|
|
|
|
public SplitToWorkspaceController getSplitToWorkspaceController() {
|
|
return mSplitToWorkspaceController;
|
|
}
|
|
|
|
@Override
|
|
protected void handleSplitAnimationGoingToHome(StatsLogManager.EventEnum splitDismissReason) {
|
|
super.handleSplitAnimationGoingToHome(splitDismissReason);
|
|
mSplitSelectStateController.getSplitAnimationController()
|
|
.playPlaceholderDismissAnim(this, splitDismissReason);
|
|
}
|
|
|
|
@Override
|
|
public void dismissSplitSelection(StatsLogManager.LauncherEvent splitDismissEvent) {
|
|
super.dismissSplitSelection(splitDismissEvent);
|
|
mSplitSelectStateController.getSplitAnimationController()
|
|
.playPlaceholderDismissAnim(this, splitDismissEvent);
|
|
}
|
|
|
|
@Override
|
|
public OverviewActionsView<?> getActionsView() {
|
|
return mActionsView;
|
|
}
|
|
|
|
@Override
|
|
protected void closeOpenViews(boolean animate) {
|
|
super.closeOpenViews(animate);
|
|
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
|
|
}
|
|
|
|
@Override
|
|
public void collectStateHandlers(List<StateHandler<LauncherState>> out) {
|
|
super.collectStateHandlers(out);
|
|
out.add(getDepthController());
|
|
out.add(new RecentsViewStateController(this));
|
|
}
|
|
|
|
public DepthController getDepthController() {
|
|
return mDepthController;
|
|
}
|
|
|
|
@Nullable
|
|
public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
|
|
return mUnfoldTransitionProgressProvider;
|
|
}
|
|
|
|
@Override
|
|
public boolean supportsAdaptiveIconAnimation(View clickedView) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public float[] getNormalOverviewScaleAndOffset() {
|
|
return DisplayController.getNavigationMode(this).hasGestures
|
|
? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET};
|
|
}
|
|
|
|
@Override
|
|
public void finishBindingItems(IntSet pagesBoundFirst) {
|
|
super.finishBindingItems(pagesBoundFirst);
|
|
// Instantiate and initialize WellbeingModel now that its loading won't interfere with
|
|
// populating workspace.
|
|
// TODO: Find a better place for this
|
|
WellbeingModel.INSTANCE.get(this);
|
|
|
|
if (mLauncherUnfoldAnimationController != null) {
|
|
// This is needed in case items are rebound while the unfold animation is in progress.
|
|
mLauncherUnfoldAnimationController.updateRegisteredViewsIfNeeded();
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
|
|
ActivityOptionsWrapper activityOptions = mAppTransitionManager.getActivityLaunchOptions(
|
|
v, item != null ? item : (ItemInfo) v.getTag());
|
|
if (mLastTouchUpTime > 0) {
|
|
activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER,
|
|
mLastTouchUpTime);
|
|
}
|
|
if (item != null && (item.animationType == DEFAULT_NO_ICON
|
|
|| item.animationType == VIEW_BACKGROUND)) {
|
|
activityOptions.options.setSplashScreenStyle(
|
|
SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
|
|
} else {
|
|
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
|
|
}
|
|
activityOptions.options.setLaunchDisplayId(
|
|
(v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
|
|
: Display.DEFAULT_DISPLAY);
|
|
activityOptions.options.setPendingIntentBackgroundActivityStartMode(
|
|
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
|
return activityOptions;
|
|
}
|
|
|
|
@Override
|
|
public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
|
|
RunnableList callbacks = new RunnableList();
|
|
ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0);
|
|
options.setSplashScreenStyle(splashScreenStyle);
|
|
options.setPendingIntentBackgroundActivityStartMode(
|
|
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
|
|
|
IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
|
|
options.setOnAnimationAbortListener(endCallback);
|
|
options.setOnAnimationFinishedListener(endCallback);
|
|
return new ActivityOptionsWrapper(options, callbacks);
|
|
}
|
|
|
|
@Override
|
|
@BinderThread
|
|
public void enterStageSplitFromRunningApp(boolean leftOrTop) {
|
|
mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
|
|
super.onDisplayInfoChanged(context, info, flags);
|
|
// When changing screens, force moving to rest state similar to StatefulActivity.onStop, as
|
|
// StatefulActivity isn't called consistently.
|
|
if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
|
|
// Do not animate moving to rest state, as it can clash with Launcher#onIdpChanged
|
|
// where reapplyUi calls StateManager's reapplyState during the state change animation,
|
|
// and cancel the state change unexpectedly. The screen will be off during screen
|
|
// transition, hiding the unanimated transition.
|
|
getStateManager().moveToRestState(/* isAnimated = */false);
|
|
}
|
|
|
|
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
|
|
getDragLayer().recreateControllers();
|
|
if (mActionsView != null) {
|
|
mActionsView.updateVerticalMargin(info.getNavigationMode());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
|
|
// If Launcher shuts downs during split select, we save some extra data in the recovery
|
|
// bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't
|
|
// work in this case because restoring straight to OverviewSplitSelect without staging data,
|
|
// or before the tasks themselves have loaded into Overview, causes a crash. So we tell
|
|
// Launcher to first restore into Overview state, wait for the relevant tasks and icons to
|
|
// load in, and then proceed to OverviewSplitSelect.
|
|
if (isInState(OVERVIEW_SPLIT_SELECT)) {
|
|
// Launcher will restart in Overview and then transition to OverviewSplitSelect.
|
|
outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
|
|
new PendingSplitSelectInfo(
|
|
mSplitSelectStateController.getInitialTaskId(),
|
|
mSplitSelectStateController.getActiveSplitStagePosition(),
|
|
mSplitSelectStateController.getSplitEvent())
|
|
));
|
|
outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When Launcher restarts, it sometimes needs to recover to a split selection state.
|
|
* This function checks if such a recovery is needed.
|
|
* @return a boolean representing whether the launcher is waiting to recover to
|
|
* OverviewSplitSelect state.
|
|
*/
|
|
public boolean hasPendingSplitSelectInfo() {
|
|
return mPendingSplitSelectInfo != null;
|
|
}
|
|
|
|
/**
|
|
* See {@link #hasPendingSplitSelectInfo()}
|
|
*/
|
|
public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() {
|
|
return mPendingSplitSelectInfo;
|
|
}
|
|
|
|
/**
|
|
* When the launcher has successfully recovered to OverviewSplitSelect state, this function
|
|
* deletes the recovery data, returning it to a null state.
|
|
*/
|
|
public void finishSplitSelectRecovery() {
|
|
mPendingSplitSelectInfo = null;
|
|
}
|
|
|
|
/**
|
|
* Sets flag whether a predictive back-to-home animation is in progress
|
|
*/
|
|
public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
|
|
mIsPredictiveBackToHomeInProgress = isInProgress;
|
|
mTISBindHelper.setPredictiveBackToHomeInProgress(isInProgress);
|
|
}
|
|
|
|
public boolean getPredictiveBackToHomeInProgress() {
|
|
return mIsPredictiveBackToHomeInProgress;
|
|
}
|
|
|
|
@Override
|
|
public boolean areDesktopTasksVisible() {
|
|
return DesktopVisibilityController.INSTANCE.get(this)
|
|
.isInDesktopModeAndNotInOverview(getDisplayId());
|
|
}
|
|
|
|
@Override
|
|
public void dispatchDeviceProfileChanged() {
|
|
super.dispatchDeviceProfileChanged();
|
|
Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged",
|
|
getDeviceProfile().toSmallString());
|
|
SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
|
|
TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
|
|
if (taskbarManager != null) {
|
|
taskbarManager.debugTaskbarManager("QuickstepLauncher#onDeviceProfileChanged");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Launches the given {@link SplitTask} in splitscreen.
|
|
*/
|
|
public void launchSplitTasks(
|
|
@NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) {
|
|
mSplitSelectStateController.launchExistingSplitPair(null /* launchingTaskView */,
|
|
splitTask.getTopLeftTask().key.id,
|
|
splitTask.getBottomRightTask().key.id,
|
|
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
|
|
/* callback= */ success -> mSplitSelectStateController.resetState(),
|
|
/* freezeTaskList= */ false,
|
|
splitTask.getSplitBounds().snapPosition,
|
|
remoteTransition);
|
|
}
|
|
|
|
/**
|
|
* Launches two apps as an app pair.
|
|
*/
|
|
public void launchAppPair(AppPairIcon appPairIcon) {
|
|
// Potentially show the Taskbar education once the app pair launch finishes
|
|
mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon,
|
|
CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE,
|
|
(success) -> {
|
|
if (success && mTaskbarUIController != null) {
|
|
mTaskbarUIController.showEduOnAppLaunch();
|
|
}
|
|
});
|
|
}
|
|
|
|
public boolean canStartHomeSafely() {
|
|
OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
|
|
return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely();
|
|
}
|
|
|
|
@Override
|
|
public boolean isBubbleBarEnabled() {
|
|
return (mTaskbarUIController != null && mTaskbarUIController.isBubbleBarEnabled());
|
|
}
|
|
|
|
@Override
|
|
public boolean hasBubbles() {
|
|
return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
|
|
}
|
|
|
|
@Override
|
|
public boolean handleIncorrectSplitTargetSelection() {
|
|
if (!mSplitSelectStateController.isSplitSelectActive()) {
|
|
return false;
|
|
}
|
|
mSplitSelectStateController.getSplitInstructionsView().goBoing();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void showShortcutBubble(ShortcutInfo info) {
|
|
if (info == null) return;
|
|
SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info);
|
|
}
|
|
|
|
@Override
|
|
public void showAppBubble(Intent intent, UserHandle user) {
|
|
if (intent == null || intent.getPackage() == null) return;
|
|
SystemUiProxy.INSTANCE.get(this).showAppBubble(intent, user);
|
|
}
|
|
|
|
/** Sets the location of the bubble bar */
|
|
public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
|
|
mBubbleBarLocation = bubbleBarLocation;
|
|
}
|
|
|
|
@Override
|
|
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
|
|
super.dump(prefix, fd, writer, args);
|
|
if (mDepthController != null) {
|
|
mDepthController.dump(prefix, writer);
|
|
}
|
|
RecentsView recentsView = getOverviewPanel();
|
|
writer.println("\nQuickstepLauncher:");
|
|
writer.println(prefix + "\tmOrientationState: " + (recentsView == null ? "recentsNull" :
|
|
recentsView.getPagedViewOrientedState()));
|
|
if (recentsView != null) {
|
|
recentsView.getSplitSelectController().dump(prefix, writer);
|
|
}
|
|
if (mAppTransitionManager != null) {
|
|
mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer);
|
|
}
|
|
if (mHotseatPredictionController != null) {
|
|
mHotseatPredictionController.dump(prefix, writer);
|
|
}
|
|
PredictionRowView<?> predictionRowView =
|
|
getAppsView().getFloatingHeaderView().findFixedRowByType(
|
|
PredictionRowView.class);
|
|
predictionRowView.dump(prefix, writer);
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
|
|
switch (name) {
|
|
case "TextClock", "android.widget.TextClock" -> {
|
|
TextClock tc = new TextClock(context, attrs);
|
|
tc.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this));
|
|
return tc;
|
|
}
|
|
case "AnalogClock", "android.widget.AnalogClock" -> {
|
|
AnalogClock ac = new AnalogClock(context, attrs);
|
|
ac.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this));
|
|
return ac;
|
|
}
|
|
}
|
|
return super.onCreateView(parent, name, context, attrs);
|
|
}
|
|
|
|
@Override
|
|
public boolean isRecentsViewVisible() {
|
|
return getStateManager().getState().isRecentsViewVisible;
|
|
}
|
|
|
|
public boolean isCanShowAllAppsEducationView() {
|
|
return mCanShowAllAppsEducationView;
|
|
}
|
|
|
|
public void setCanShowAllAppsEducationView(boolean canShowAllAppsEducationView) {
|
|
mCanShowAllAppsEducationView = canShowAllAppsEducationView;
|
|
}
|
|
|
|
@Override
|
|
public void returnToHomescreen() {
|
|
getStateManager().goToState(LauncherState.NORMAL);
|
|
}
|
|
}
|