Snap for 12210778 from f3a85e17af to 24Q4-release

Change-Id: Ia2cf4a909f7c73669e44a1b951d9d2f3313287a3
This commit is contained in:
Android Build Coastguard Worker
2024-08-10 01:25:31 +00:00
25 changed files with 431 additions and 56 deletions
@@ -25,6 +25,7 @@ import android.os.Debug;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
@@ -38,6 +39,7 @@ import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
/**
* Controls the visibility of the workspace and the resumed / paused state when desktop mode
@@ -56,7 +58,7 @@ public class DesktopVisibilityController {
private boolean mGestureInProgress;
@Nullable
private IDesktopTaskListener mDesktopTaskListener;
private DesktopTaskListenerImpl mDesktopTaskListener;
public DesktopVisibilityController(Launcher launcher) {
mLauncher = launcher;
@@ -66,24 +68,7 @@ public class DesktopVisibilityController {
* Register a listener with System UI to receive updates about desktop tasks state
*/
public void registerSystemUiListener() {
mDesktopTaskListener = new IDesktopTaskListener.Stub() {
@Override
public void onTasksVisibilityChanged(int displayId, int visibleTasksCount) {
MAIN_EXECUTOR.execute(() -> {
if (displayId == mLauncher.getDisplayId()) {
if (DEBUG) {
Log.d(TAG, "desktop visible tasks count changed=" + visibleTasksCount);
}
setVisibleDesktopTasksCount(visibleTasksCount);
}
});
}
@Override
public void onStashedChanged(int displayId, boolean stashed) {
Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated");
}
};
mDesktopTaskListener = new DesktopTaskListenerImpl(this, mLauncher.getDisplayId());
SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener);
}
@@ -92,6 +77,7 @@ public class DesktopVisibilityController {
*/
public void unregisterSystemUiListener() {
SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null);
mDesktopTaskListener.release();
mDesktopTaskListener = null;
}
@@ -355,4 +341,43 @@ public class DesktopVisibilityController {
*/
void onDesktopVisibilityChanged(boolean visible);
}
/**
* Wrapper for the IDesktopTaskListener stub to prevent lingering references to the launcher
* activity via the controller.
*/
private static class DesktopTaskListenerImpl extends IDesktopTaskListener.Stub {
private DesktopVisibilityController mController;
private final int mDisplayId;
DesktopTaskListenerImpl(@NonNull DesktopVisibilityController controller, int displayId) {
mController = controller;
mDisplayId = displayId;
}
/**
* Clears any references to the controller.
*/
void release() {
mController = null;
}
@Override
public void onTasksVisibilityChanged(int displayId, int visibleTasksCount) {
MAIN_EXECUTOR.execute(() -> {
if (mController != null && displayId == mDisplayId) {
if (DEBUG) {
Log.d(TAG, "desktop visible tasks count changed=" + visibleTasksCount);
}
mController.setVisibleDesktopTasksCount(visibleTasksCount);
}
});
}
@Override
public void onStashedChanged(int displayId, boolean stashed) {
Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated");
}
}
}
@@ -16,19 +16,24 @@
package com.android.launcher3.taskbar;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
import java.util.ArrayList;
import java.util.List;
// TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar.
/** Base for common behavior between taskbar window contexts. */
public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext {
public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext,
SystemShortcut.BubbleActivityStarter {
protected final LayoutInflater mLayoutInflater;
private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
@@ -48,6 +53,18 @@ public abstract class BaseTaskbarContext extends ContextThemeWrapper implements
return mDPChangeListeners;
}
@Override
public void showShortcutBubble(ShortcutInfo info) {
if (info == null) return;
SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info);
}
@Override
public void showAppBubble(Intent intent) {
if (intent == null || intent.getPackage() == null) return;
SystemUiProxy.INSTANCE.get(this).showAppBubble(intent);
}
/** Callback invoked when a drag is initiated within this context. */
public abstract void onDragStart();
@@ -225,9 +225,12 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
// Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so
// avoid updating taskbar state in that situation (when it's non-interactive -- or
// "background") to avoid premature animations.
if (ENABLE_SHELL_TRANSITIONS && isVisible
&& mLauncher.getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)
&& !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
LauncherState state = mLauncher.getStateManager().getState();
boolean nonInteractiveState = state.hasFlag(FLAG_NON_INTERACTIVE)
&& !state.isTaskbarAlignedWithHotseat(mLauncher);
if ((ENABLE_SHELL_TRANSITIONS
&& isVisible
&& (nonInteractiveState || mSkipLauncherVisibilityChange))) {
return null;
}
@@ -212,7 +212,7 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT
* stashed handle to wrap around the hotseat items.
*/
public Animator createRevealAnimToIsStashed(boolean isStashed, Rect taskbarToHotseatOffsets) {
Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds());
Rect visualBounds = mControllers.taskbarViewController.getIconLayoutVisualBounds();
float startRadius = mStashedHandleRadius;
if (DisplayController.isTransientTaskbar(mActivity)) {
@@ -927,7 +927,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
fromInit);
boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
onNotificationShadeExpandChanged(isShadeVisible, fromInit);
onNotificationShadeExpandChanged(isShadeVisible, fromInit || isPhoneMode());
mControllers.taskbarViewController.setRecentsButtonDisabled(
mControllers.navbarButtonsViewController.isRecentsDisabled()
|| isNavBarKidsModeActive());
@@ -85,6 +85,9 @@ public class TaskbarForceVisibleImmersiveController implements TouchController {
/** Update values tracked via sysui flags. */
public void updateSysuiFlags(@SystemUiStateFlags long sysuiFlags) {
if (mContext.isPhoneMode()) {
return;
}
mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0;
if (mContext.isNavBarForceVisible()) {
if (mIsImmersiveMode) {
@@ -53,6 +53,7 @@ import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LogUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -69,6 +70,9 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
private static final SystemShortcut.Factory<BaseTaskbarContext>
APP_INFO = SystemShortcut.AppInfo::new;
private static final SystemShortcut.Factory<BaseTaskbarContext>
BUBBLE = SystemShortcut.BubbleShortcut::new;
private final TaskbarActivityContext mContext;
private final PopupDataProvider mPopupDataProvider;
@@ -182,10 +186,13 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
// Create a Stream of all applicable system shortcuts
private Stream<SystemShortcut.Factory> getSystemShortcuts() {
// append split options to APP_INFO shortcut, the order here will reflect in the popup
return Stream.concat(
Stream.of(APP_INFO),
mControllers.uiController.getSplitMenuOptions()
);
ArrayList<SystemShortcut.Factory> shortcuts = new ArrayList<>();
shortcuts.add(APP_INFO);
shortcuts.addAll(mControllers.uiController.getSplitMenuOptions().toList());
if (com.android.wm.shell.Flags.enableBubbleAnything()) {
shortcuts.add(BUBBLE);
}
return shortcuts.stream();
}
@Override
@@ -28,6 +28,7 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -86,6 +87,10 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa
* Updates the scrim state based on the flags.
*/
public void updateStateForSysuiFlags(@SystemUiStateFlags long stateFlags, boolean skipAnim) {
if (mActivity.isPhoneMode()) {
// There is no scrim for the bar in the phone mode.
return;
}
if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
// These scrims aren't used if bubble bar & transient taskbar are active.
return;
@@ -97,10 +102,20 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa
private boolean shouldShowScrim() {
final boolean bubblesExpanded = (mSysUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
boolean isShadeVisible = (mSysUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
BubbleControllers bubbleControllers = mActivity.getBubbleControllers();
boolean isBubbleControllersPresented = bubbleControllers != null;
// when the taskbar is in persistent mode, we hide the task bar icons on bubble bar expand,
// which makes the taskbar invisible, so need to check if the bubble bar is not on home
// to show the scrim view
boolean showScrimForBubbles = bubblesExpanded
&& !mTaskbarVisible
&& isBubbleControllersPresented
&& !DisplayController.isTransientTaskbar(mActivity)
&& !bubbleControllers.bubbleStashController.isBubblesShowingOnHome();
return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
&& !isShadeVisible
&& !mControllers.taskbarStashController.isStashed()
&& mTaskbarVisible;
&& (mTaskbarVisible || showScrimForBubbles);
}
private float getScrimAlpha() {
@@ -1022,6 +1022,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
/** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */
public void updateStateForSysuiFlags(long systemUiStateFlags, boolean skipAnim) {
if (mActivity.isPhoneMode()) {
return;
}
long animDuration = TASKBAR_STASH_DURATION;
long startDelay = 0;
@@ -61,6 +61,8 @@ public class TaskbarUIController {
// Initialized in init.
protected TaskbarControllers mControllers;
protected boolean mSkipLauncherVisibilityChange;
@CallSuper
protected void init(TaskbarControllers taskbarControllers) {
mControllers = taskbarControllers;
@@ -418,4 +420,12 @@ public class TaskbarUIController {
public void setUserIsNotGoingHome(boolean isNotGoingHome) {
mControllers.taskbarStashController.setUserIsNotGoingHome(isNotGoingHome);
}
/**
* Sets whether to prevent taskbar from reacting to launcher visibility during the recents
* transition animation.
*/
public void setSkipLauncherVisibilityChange(boolean skip) {
mSkipLauncherVisibilityChange = skip;
}
}
@@ -669,8 +669,20 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates);
}
/**
* Gets visual bounds of the taskbar view. The visual bounds correspond to the taskbar touch
* area, rather than layout placement in the parent view.
*/
public Rect getIconLayoutVisualBounds() {
return new Rect(mIconLayoutBounds);
}
/** Gets taskbar layout bounds in parent view. */
public Rect getIconLayoutBounds() {
return mIconLayoutBounds;
Rect actualBounds = new Rect(mIconLayoutBounds);
actualBounds.top = getTop();
actualBounds.bottom = getBottom();
return actualBounds;
}
/**
@@ -96,7 +96,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5;
public static final int ALPHA_INDEX_SMALL_SCREEN = 6;
private static final int NUM_ALPHA_CHANNELS = 7;
public static final int ALPHA_INDEX_BUBBLE_BAR = 7;
private static final int NUM_ALPHA_CHANNELS = 8;
private static boolean sEnableModelLoadingForTests = true;
@@ -272,6 +274,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
OneShotPreDrawListener.add(mTaskbarView, listener);
}
public Rect getIconLayoutVisualBounds() {
return mTaskbarView.getIconLayoutVisualBounds();
}
public Rect getIconLayoutBounds() {
return mTaskbarView.getIconLayoutBounds();
}
@@ -462,14 +468,14 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
if (mControllers.getSharedState().startTaskbarVariantIsTransient) {
float transY =
mTransientTaskbarDp.taskbarBottomMargin + (mTransientTaskbarDp.taskbarHeight
- mTaskbarView.getIconLayoutBounds().bottom)
- mTaskbarView.getIconLayoutVisualBounds().bottom)
- (mPersistentTaskbarDp.taskbarHeight
- mTransientTaskbarDp.taskbarIconSize) / 2f;
taskbarIconTranslationYForPinningValue = mapRange(scale, 0f, transY);
} else {
float transY =
-mTransientTaskbarDp.taskbarBottomMargin + (mPersistentTaskbarDp.taskbarHeight
- mTaskbarView.getIconLayoutBounds().bottom)
- mTaskbarView.getIconLayoutVisualBounds().bottom)
- (mTransientTaskbarDp.taskbarHeight
- mTransientTaskbarDp.taskbarIconSize) / 2f;
taskbarIconTranslationYForPinningValue = mapRange(scale, transY, 0f);
@@ -110,7 +110,7 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
}
fun setIsVoiceInteractionWindowVisible(visible: Boolean, skipAnim: Boolean) {
if (isVoiceInteractionWindowVisible == visible) {
if (isVoiceInteractionWindowVisible == visible || context.isPhoneMode) {
return
}
isVoiceInteractionWindowVisible = visible
@@ -661,13 +661,25 @@ public class BubbleBarView extends FrameLayout {
return displayHeight - bubbleBarHeight + (int) mController.getBubbleBarTranslationY();
}
/**
* Updates the bounds with translation that may have been applied and returns the result.
*/
/** Returns the bounds with translation that may have been applied. */
public Rect getBubbleBarBounds() {
mBubbleBarBounds.top = getTop() + (int) getTranslationY() + mPointerSize;
mBubbleBarBounds.bottom = getBottom() + (int) getTranslationY();
return mBubbleBarBounds;
Rect bounds = new Rect(mBubbleBarBounds);
bounds.top = getTop() + (int) getTranslationY() + mPointerSize;
bounds.bottom = getBottom() + (int) getTranslationY();
return bounds;
}
/** Returns the expanded bounds with translation that may have been applied. */
public Rect getBubbleBarExpandedBounds() {
Rect expandedBounds = getBubbleBarBounds();
if (!isExpanded() || isExpanding()) {
if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
expandedBounds.right = expandedBounds.left + (int) expandedWidth();
} else {
expandedBounds.left = expandedBounds.right - (int) expandedWidth();
}
}
return expandedBounds;
}
/**
@@ -1279,6 +1291,13 @@ public class BubbleBarView extends FrameLayout {
return mIsBarExpanded;
}
/**
* Returns whether the bubble bar is expanding.
*/
public boolean isExpanding() {
return mWidthAnimator.isRunning() && mIsBarExpanded;
}
/**
* Get width of the bubble bar as if it would be expanded.
*
@@ -72,6 +72,7 @@ public class BubbleBarViewController {
private BubbleDragController mBubbleDragController;
private TaskbarStashController mTaskbarStashController;
private TaskbarInsetsController mTaskbarInsetsController;
private TaskbarViewPropertiesProvider mTaskbarViewPropertiesProvider;
private View.OnClickListener mBubbleClickListener;
private View.OnClickListener mBubbleBarClickListener;
private BubbleView.Controller mBubbleViewController;
@@ -110,13 +111,16 @@ public class BubbleBarViewController {
R.dimen.bubblebar_icon_size);
}
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
/** Initializes controller. */
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers,
TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) {
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
mBubbleDragController = bubbleControllers.bubbleDragController;
mTaskbarStashController = controllers.taskbarStashController;
mTaskbarInsetsController = controllers.taskbarInsetsController;
mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
mTaskbarViewPropertiesProvider = taskbarViewPropertiesProvider;
onBubbleBarConfigurationChanged(/* animate= */ false);
mActivity.addOnDeviceProfileChangeListener(
dp -> onBubbleBarConfigurationChanged(/* animate= */ true));
@@ -348,6 +352,7 @@ public class BubbleBarViewController {
if (hidden) {
mBarView.setAlpha(0);
mBarView.setExpanded(false);
updatePersistentTaskbar(/* isBubbleBarExpanded = */ false);
}
mActivity.bubbleBarVisibilityChanged(!hidden);
}
@@ -612,6 +617,7 @@ public class BubbleBarViewController {
public void setExpanded(boolean isExpanded) {
if (isExpanded != mBarView.isExpanded()) {
mBarView.setExpanded(isExpanded);
updatePersistentTaskbar(isExpanded);
if (!isExpanded) {
mSystemUiProxy.collapseBubbles();
} else {
@@ -622,6 +628,25 @@ public class BubbleBarViewController {
}
}
private void updatePersistentTaskbar(boolean isBubbleBarExpanded) {
if (mBubbleStashController.isTransientTaskBar()) return;
boolean hideTaskbar = isBubbleBarExpanded && isIntersectingTaskbar();
mTaskbarViewPropertiesProvider
.getIconsAlpha()
.animateToValue(hideTaskbar ? 0 : 1)
.start();
}
/** Return {@code true} if expanded bubble bar would intersect the taskbar. */
public boolean isIntersectingTaskbar() {
if (mBarView.isExpanding() || mBarView.isExpanded()) {
Rect taskbarViewBounds = mTaskbarViewPropertiesProvider.getTaskbarViewBounds();
return mBarView.getBubbleBarExpandedBounds().intersect(taskbarViewBounds);
} else {
return false;
}
}
/**
* Sets whether the bubble bar should be expanded. This method is used in response to UI events
* from SystemUI.
@@ -755,4 +780,14 @@ public class BubbleBarViewController {
pw.println(" Bubble bar view is null!");
}
}
/** Interface for BubbleBarViewController to get the taskbar view properties. */
public interface TaskbarViewPropertiesProvider {
/** Returns the bounds of the taskbar. */
Rect getTaskbarViewBounds();
/** Returns taskbar icons alpha */
MultiPropertyFactory<View>.MultiProperty getIconsAlpha();
}
}
@@ -15,8 +15,15 @@
*/
package com.android.launcher3.taskbar.bubbles;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_BUBBLE_BAR;
import android.graphics.Rect;
import android.view.View;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController.TaskbarViewPropertiesProvider;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.RunnableList;
import java.io.PrintWriter;
@@ -79,7 +86,20 @@ public class BubbleControllers {
bubbleStashedHandleViewController.orElse(null),
taskbarControllers::runAfterInit
);
bubbleBarViewController.init(taskbarControllers, /* bubbleControllers = */ this);
bubbleBarViewController.init(taskbarControllers, /* bubbleControllers = */ this,
new TaskbarViewPropertiesProvider() {
@Override
public Rect getTaskbarViewBounds() {
return taskbarControllers.taskbarViewController.getIconLayoutBounds();
}
@Override
public MultiPropertyFactory<View>.MultiProperty getIconsAlpha() {
return taskbarControllers.taskbarViewController
.getTaskbarIconAlpha()
.get(ALPHA_INDEX_BUBBLE_BAR);
}
});
bubbleDragController.init(/* bubbleControllers = */ this);
bubbleDismissController.init(/* bubbleControllers = */ this);
bubbleBarPinController.init(this);
@@ -45,6 +45,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
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;
@@ -75,6 +76,7 @@ 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;
@@ -215,7 +217,8 @@ import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class QuickstepLauncher extends Launcher implements RecentsViewContainer {
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 =
@@ -466,6 +469,9 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer
if (Flags.enablePrivateSpace()) {
shortcuts.add(UNINSTALL_APP);
}
if (com.android.wm.shell.Flags.enableBubbleAnything()) {
shortcuts.add(BUBBLE_SHORTCUT);
}
return shortcuts.stream();
}
@@ -1436,6 +1442,18 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer
return true;
}
@Override
public void showShortcutBubble(ShortcutInfo info) {
if (info == null) return;
SystemUiProxy.INSTANCE.get(this).showShortcutBubble(info);
}
@Override
public void showAppBubble(Intent intent) {
if (intent == null || intent.getPackage() == null) return;
SystemUiProxy.INSTANCE.get(this).showAppBubble(intent);
}
private static final class LauncherTaskViewController extends
TaskViewTouchController<QuickstepLauncher> {
@@ -47,7 +47,6 @@ import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.IRemoteAnimationRunner;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
@@ -894,6 +893,36 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle, SafeCloseable {
}
}
/**
* Tells SysUI to show a shortcut bubble.
*
* @param info the shortcut info used to create or identify the bubble.
*/
public void showShortcutBubble(ShortcutInfo info) {
try {
if (mBubbles != null) {
mBubbles.showShortcutBubble(info);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed call show bubble for shortcut");
}
}
/**
* Tells SysUI to show a bubble of an app.
*
* @param intent the intent used to create the bubble.
*/
public void showAppBubble(Intent intent) {
try {
if (mBubbles != null) {
mBubbles.showAppBubble(intent);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed call show bubble for app");
}
}
//
// Splitscreen
//
@@ -18,6 +18,7 @@ package com.android.quickstep;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
@@ -44,6 +45,7 @@ import androidx.annotation.UiThread;
import com.android.internal.util.ArrayUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.SystemUiFlagUtils;
@@ -334,6 +336,28 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
options.setTransientLaunch();
}
options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
// Notify taskbar that we should skip reacting to launcher visibility change to
// avoid a jumping taskbar.
TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
taskbarUIController.setSkipLauncherVisibilityChange(true);
mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
@Override
public void onRecentsAnimationCanceled(
@NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
taskbarUIController.setSkipLauncherVisibilityChange(false);
}
@Override
public void onRecentsAnimationFinished(
@NonNull RecentsAnimationController controller) {
taskbarUIController.setSkipLauncherVisibilityChange(false);
}
});
}
mRecentsAnimationStartPending = getSystemUiProxy()
.startRecentsActivity(intent, options, mCallbacks);
if (enableHandleDelayedGestureCallbacks()) {
@@ -836,7 +836,7 @@ public class SplitSelectStateController {
private final int mSplitPlaceholderSize;
private final int mSplitPlaceholderInset;
private ActivityManager.RunningTaskInfo mTaskInfo;
private ISplitSelectListener mSplitSelectListener;
private DesktopSplitSelectListenerImpl mSplitSelectListener;
private Drawable mAppIcon;
public SplitFromDesktopController(QuickstepLauncher launcher,
@@ -847,21 +847,14 @@ public class SplitSelectStateController {
R.dimen.split_placeholder_size);
mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
R.dimen.split_placeholder_inset);
mSplitSelectListener = new ISplitSelectListener.Stub() {
@Override
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
taskBounds));
return true;
}
};
mSplitSelectListener = new DesktopSplitSelectListenerImpl(this);
SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener);
}
void onDestroy() {
SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener(
mSplitSelectListener);
mSplitSelectListener.release();
mSplitSelectListener = null;
}
@@ -954,4 +947,35 @@ public class SplitSelectStateController {
}
}
}
/**
* Wrapper for the ISplitSelectListener stub to prevent lingering references to the launcher
* activity via the controller.
*/
private static class DesktopSplitSelectListenerImpl extends ISplitSelectListener.Stub {
private SplitFromDesktopController mController;
DesktopSplitSelectListenerImpl(@NonNull SplitFromDesktopController controller) {
mController = controller;
}
/**
* Clears any references to the controller.
*/
void release() {
mController = null;
}
@Override
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
MAIN_EXECUTOR.execute(() -> {
if (mController != null) {
mController.enterSplitSelect(taskInfo, splitPosition, taskBounds);
}
});
return true;
}
}
}
@@ -24,6 +24,9 @@ import com.android.launcher3.BuildConfig
import com.android.launcher3.ui.AbstractLauncherUiTest
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape
import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.launcher3.util.rule.TestStabilityRule
import com.android.launcher3.util.rule.TestStabilityRule.LOCAL
import com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Test
@@ -42,6 +45,7 @@ class TaplTestsOverviewDesktop : AbstractLauncherUiTest<QuickstepLauncher?>() {
mLauncher.goHome()
}
@TestStabilityRule.Stability(flavors = LOCAL or PLATFORM_POSTSUBMIT)
@Test
@PortraitLandscape
fun enterDesktopViaOverviewMenu() {
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M23,5v8h-2V5H3v14h10v2v0H3c-1.1,0 -2,-0.9 -2,-2V5c0,-1.1 0.9,-2 2,-2h18C22.1,3 23,3.9 23,5zM10,8v2.59L5.71,6.29L4.29,7.71L8.59,12H6v2h6V8H10zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
</vector>
+2 -1
View File
@@ -215,7 +215,8 @@
<string name="dismiss_prediction_label">Don\'t suggest app</string>
<!-- Label for pinning predicted app. -->
<string name="pin_prediction">Pin Prediction</string>
<!-- Label for bubbling a launcher item. [CHAR_LIMIT=20] -->
<string name="bubble">Bubble</string>
<!-- Permissions: -->
<skip />
@@ -25,6 +25,7 @@ import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherSettings;
@@ -97,6 +98,8 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
public int options;
@Nullable
private ShortcutInfo mShortcutInfo = null;
public WorkspaceItemInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
@@ -175,6 +178,9 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo,
@NonNull final Context context) {
if (com.android.wm.shell.Flags.enableBubbleAnything()) {
mShortcutInfo = shortcutInfo;
}
// {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
intent = ShortcutKey.makeIntent(shortcutInfo);
title = shortcutInfo.getShortLabel();
@@ -204,6 +210,11 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
: Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
@Nullable
public ShortcutInfo getDeepShortcutInfo() {
return mShortcutInfo;
}
/**
* {@code true} if the shortcut is disabled due to its app being a lower version.
*/
@@ -11,9 +11,11 @@ import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
@@ -25,6 +27,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.AbstractFloatingViewHelper;
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.SecondaryDropTarget;
import com.android.launcher3.Utilities;
@@ -53,6 +56,7 @@ import java.util.Arrays;
*/
public abstract class SystemShortcut<T extends ActivityContext> extends ItemInfo
implements View.OnClickListener {
private static final String TAG = "SystemShortcut";
private final int mIconResId;
protected final int mLabelResId;
@@ -383,4 +387,63 @@ public abstract class SystemShortcut<T extends ActivityContext> extends ItemInfo
mAbstractFloatingViewHelper.closeOpenViews(mTarget, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
public static final Factory<ActivityContext> BUBBLE_SHORTCUT =
(activity, itemInfo, originalView) -> {
if ((itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
&& (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION)
&& !(itemInfo instanceof WorkspaceItemInfo)) {
return null;
}
return new BubbleShortcut(activity, itemInfo, originalView);
};
public interface BubbleActivityStarter {
/** Tell SysUI to show the provided shortcut in a bubble. */
void showShortcutBubble(ShortcutInfo info);
/** Tell SysUI to show the provided intent in a bubble. */
void showAppBubble(Intent intent);
}
public static class BubbleShortcut<T extends ActivityContext> extends SystemShortcut<T> {
private BubbleActivityStarter mStarter;
public BubbleShortcut(T target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_bubble_button, R.string.bubble, target,
itemInfo, originalView);
if (target instanceof BubbleActivityStarter) {
mStarter = (BubbleActivityStarter) target;
}
}
@Override
public void onClick(View view) {
dismissTaskMenuView();
if (mStarter == null) {
Log.w(TAG, "starter null!");
return;
}
// TODO: handle GroupTask (single) items so that recent items in taskbar work
if (mItemInfo instanceof WorkspaceItemInfo) {
WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
ShortcutInfo shortcutInfo = workspaceItemInfo.getDeepShortcutInfo();
if (shortcutInfo != null) {
mStarter.showShortcutBubble(shortcutInfo);
return;
}
}
// If we're here check for an intent
Intent intent = mItemInfo.getIntent();
if (intent != null) {
if (intent.getPackage() == null) {
intent.setPackage(mItemInfo.getTargetPackage());
}
mStarter.showAppBubble(intent);
} else {
Log.w(TAG, "unable to bubble, no intent: " + mItemInfo);
}
}
}
}