diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 725b20525bf7e..bec6844ae8631 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -24,6 +24,7 @@ import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -37,7 +38,6 @@ import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_LONGER_BUBBLE;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
-import static com.android.wm.shell.floating.FloatingTasksController.SHOW_FLOATING_TASKS_AS_BUBBLES;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -150,6 +150,9 @@ public class BubbleController implements ConfigurationChangeListener {
private final ShellExecutor mBackgroundExecutor;
+ // Whether or not we should show bubbles pinned at the bottom of the screen.
+ private boolean mIsBubbleBarEnabled;
+
private BubbleLogger mLogger;
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
@@ -210,7 +213,6 @@ public class BubbleController implements ConfigurationChangeListener {
/** Drag and drop controller to register listener for onDragStarted. */
private DragAndDropController mDragAndDropController;
-
public BubbleController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -526,6 +528,12 @@ public class BubbleController implements ConfigurationChangeListener {
mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
}
+ // TODO(b/256873975): Should pass this into the constructor once flags are available to shell.
+ /** Sets whether the bubble bar is enabled (i.e. bubbles pinned to bottom on large screens). */
+ public void setBubbleBarEnabled(boolean enabled) {
+ mIsBubbleBarEnabled = enabled;
+ }
+
/** Whether this userId belongs to the current user. */
private boolean isCurrentProfile(int userId) {
return userId == UserHandle.USER_ALL
@@ -591,7 +599,8 @@ public class BubbleController implements ConfigurationChangeListener {
}
mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation);
}
- if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubblePositioner.isLargeScreen()) {
+
+ if (mIsBubbleBarEnabled && mBubblePositioner.isLargeScreen()) {
mBubblePositioner.setUsePinnedLocation(true);
} else {
mBubblePositioner.setUsePinnedLocation(false);
@@ -959,14 +968,18 @@ public class BubbleController implements ConfigurationChangeListener {
}
/**
- * Adds a bubble for a specific intent. These bubbles are not backed by a notification
- * and remain until the user dismisses the bubble or bubble stack. Only one intent bubble
- * is supported at a time.
+ * Adds and expands bubble for a specific intent. These bubbles are not backed by a n
+ * otification and remain until the user dismisses the bubble or bubble stack. Only one intent
+ * bubble is supported at a time.
*
* @param intent the intent to display in the bubble expanded view.
*/
- public void addAppBubble(Intent intent) {
+ public void showAppBubble(Intent intent) {
if (intent == null || intent.getPackage() == null) return;
+
+ PackageManager packageManager = getPackageManagerForUser(mContext, mCurrentUserId);
+ if (!isResizableActivity(intent, packageManager, KEY_APP_BUBBLE)) return;
+
Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor);
b.setShouldAutoExpand(true);
inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
@@ -1489,18 +1502,23 @@ public class BubbleController implements ConfigurationChangeListener {
}
PackageManager packageManager = getPackageManagerForUser(
context, entry.getStatusBarNotification().getUser().getIdentifier());
- ActivityInfo info =
- intent.getIntent().resolveActivityInfo(packageManager, 0);
+ return isResizableActivity(intent.getIntent(), packageManager, entry.getKey());
+ }
+
+ static boolean isResizableActivity(Intent intent, PackageManager packageManager, String key) {
+ if (intent == null) {
+ Log.w(TAG, "Unable to send as bubble: " + key + " null intent");
+ return false;
+ }
+ ActivityInfo info = intent.resolveActivityInfo(packageManager, 0);
if (info == null) {
- Log.w(TAG, "Unable to send as bubble, "
- + entry.getKey() + " couldn't find activity info for intent: "
- + intent);
+ Log.w(TAG, "Unable to send as bubble: " + key
+ + " couldn't find activity info for intent: " + intent);
return false;
}
if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- Log.w(TAG, "Unable to send as bubble, "
- + entry.getKey() + " activity is not resizable for intent: "
- + intent);
+ Log.w(TAG, "Unable to send as bubble: " + key
+ + " activity is not resizable for intent: " + intent);
return false;
}
return true;
@@ -1673,6 +1691,13 @@ public class BubbleController implements ConfigurationChangeListener {
});
}
+ @Override
+ public void showAppBubble(Intent intent) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.showAppBubble(intent);
+ });
+ }
+
@Override
public boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List children, IntConsumer removeCallback,
@@ -1783,6 +1808,13 @@ public class BubbleController implements ConfigurationChangeListener {
});
}
+ @Override
+ public void setBubbleBarEnabled(boolean enabled) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.setBubbleBarEnabled(enabled);
+ });
+ }
+
@Override
public void onNotificationPanelExpandedChanged(boolean expanded) {
mMainExecutor.execute(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 7f891ec6d2150..465d1abe0a3df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -22,6 +22,7 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.NotificationChannel;
+import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
@@ -107,6 +108,15 @@ public interface Bubbles {
*/
void expandStackAndSelectBubble(Bubble bubble);
+ /**
+ * Adds and expands bubble that is not notification based, but instead based on an intent from
+ * the app. The intent must be explicit (i.e. include a package name or fully qualified
+ * component class name) and the activity for it should be resizable.
+ *
+ * @param intent the intent to populate the bubble.
+ */
+ void showAppBubble(Intent intent);
+
/**
* @return a bubble that matches the provided shortcutId, if one exists.
*/
@@ -232,6 +242,11 @@ public interface Bubbles {
*/
void onUserRemoved(int removedUserId);
+ /**
+ * Sets whether bubble bar should be enabled or not.
+ */
+ void setBubbleBarEnabled(boolean enabled);
+
/** Listener to find out about stack expansion / collapse events. */
interface BubbleExpandListener {
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 625d8a83736d1..962be9da2111b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -24,7 +24,6 @@ import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
@@ -65,8 +64,6 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.floating.FloatingTasks;
-import com.android.wm.shell.floating.FloatingTasksController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -574,47 +571,6 @@ public abstract class WMShellBaseModule {
return Optional.empty();
}
- //
- // Floating tasks
- //
-
- @WMSingleton
- @Provides
- static Optional provideFloatingTasks(
- Optional floatingTaskController) {
- return floatingTaskController.map((controller) -> controller.asFloatingTasks());
- }
-
- @WMSingleton
- @Provides
- static Optional provideFloatingTasksController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellCommandHandler shellCommandHandler,
- Optional bubbleController,
- WindowManager windowManager,
- ShellTaskOrganizer organizer,
- TaskViewTransitions taskViewTransitions,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellBackgroundThread ShellExecutor bgExecutor,
- SyncTransactionQueue syncQueue) {
- if (FloatingTasksController.FLOATING_TASKS_ENABLED) {
- return Optional.of(new FloatingTasksController(context,
- shellInit,
- shellController,
- shellCommandHandler,
- bubbleController,
- windowManager,
- organizer,
- taskViewTransitions,
- mainExecutor,
- bgExecutor,
- syncQueue));
- } else {
- return Optional.empty();
- }
- }
-
//
// Starting window
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java
deleted file mode 100644
index 83a1734dc71ab..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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.wm.shell.floating;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.floating.views.FloatingTaskView;
-
-import java.util.Objects;
-
-/**
- * Controls a floating dismiss circle that has a 'magnetic' field around it, causing views moved
- * close to the target to be stuck to it unless moved out again.
- */
-public class FloatingDismissController {
-
- /** Velocity required to dismiss the view without dragging it into the dismiss target. */
- private static final float FLING_TO_DISMISS_MIN_VELOCITY = 4000f;
- /**
- * Max velocity that the view can be moving through the target with to stick (i.e. if it's
- * more than this velocity, it will pass through the target.
- */
- private static final float STICK_TO_TARGET_MAX_X_VELOCITY = 2000f;
- /**
- * Percentage of the target width to use to determine if an object flung towards the target
- * should dismiss (e.g. if target is 100px and this is set ot 2f, anything flung within a
- * 200px-wide area around the target will be considered 'near' enough get dismissed).
- */
- private static final float FLING_TO_TARGET_WIDTH_PERCENT = 2f;
- /** Minimum alpha to apply to the view being dismissed when it is in the target. */
- private static final float DISMISS_VIEW_MIN_ALPHA = 0.6f;
- /** Amount to scale down the view being dismissed when it is in the target. */
- private static final float DISMISS_VIEW_SCALE_DOWN_PERCENT = 0.15f;
-
- private Context mContext;
- private FloatingTasksController mController;
- private FloatingTaskLayer mParent;
-
- private DismissView mDismissView;
- private ValueAnimator mDismissAnimator;
- private View mViewBeingDismissed;
- private float mDismissSizePercent;
- private float mDismissSize;
-
- /**
- * The currently magnetized object, which is being dragged and will be attracted to the magnetic
- * dismiss target.
- */
- private MagnetizedObject mMagnetizedObject;
- /**
- * The MagneticTarget instance for our circular dismiss view. This is added to the
- * MagnetizedObject instances for the view being dragged.
- */
- private MagnetizedObject.MagneticTarget mMagneticTarget;
- /** Magnet listener that handles animating and dismissing the view. */
- private MagnetizedObject.MagnetListener mFloatingViewMagnetListener;
-
- public FloatingDismissController(Context context, FloatingTasksController controller,
- FloatingTaskLayer parent) {
- mContext = context;
- mController = controller;
- mParent = parent;
- updateSizes();
- createAndAddDismissView();
-
- mDismissAnimator = ValueAnimator.ofFloat(1f, 0f);
- mDismissAnimator.addUpdateListener(animation -> {
- final float value = (float) animation.getAnimatedValue();
- if (mDismissView != null) {
- mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f);
- mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f);
- final float scaleValue = Math.max(value, mDismissSizePercent);
- mDismissView.getCircle().setScaleX(scaleValue);
- mDismissView.getCircle().setScaleY(scaleValue);
- }
- if (mViewBeingDismissed != null) {
- // TODO: alpha doesn't actually apply to taskView currently.
- mViewBeingDismissed.setAlpha(Math.max(value, DISMISS_VIEW_MIN_ALPHA));
- mViewBeingDismissed.setScaleX(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT));
- mViewBeingDismissed.setScaleY(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT));
- }
- });
-
- mFloatingViewMagnetListener = new MagnetizedObject.MagnetListener() {
- @Override
- public void onStuckToTarget(
- @NonNull MagnetizedObject.MagneticTarget target) {
- animateDismissing(/* dismissing= */ true);
- }
-
- @Override
- public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
- float velX, float velY, boolean wasFlungOut) {
- animateDismissing(/* dismissing= */ false);
- mParent.onUnstuckFromTarget((FloatingTaskView) mViewBeingDismissed, velX, velY,
- wasFlungOut);
- }
-
- @Override
- public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- doDismiss();
- }
- };
- }
-
- /** Updates all the sizes used and applies them to the {@link DismissView}. */
- public void updateSizes() {
- Resources res = mContext.getResources();
- mDismissSize = res.getDimensionPixelSize(
- R.dimen.floating_task_dismiss_circle_size);
- final float minDismissSize = res.getDimensionPixelSize(
- R.dimen.floating_dismiss_circle_small);
- mDismissSizePercent = minDismissSize / mDismissSize;
-
- if (mDismissView != null) {
- mDismissView.updateResources();
- }
- }
-
- /** Prepares the view being dragged to be magnetic. */
- public void setUpMagneticObject(View viewBeingDragged) {
- mViewBeingDismissed = viewBeingDragged;
- mMagnetizedObject = getMagnetizedView(viewBeingDragged);
- mMagnetizedObject.clearAllTargets();
- mMagnetizedObject.addTarget(mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mFloatingViewMagnetListener);
- }
-
- /** Shows or hides the dismiss target. */
- public void showDismiss(boolean show) {
- if (show) {
- mDismissView.show();
- } else {
- mDismissView.hide();
- }
- }
-
- /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */
- public boolean passEventToMagnetizedObject(MotionEvent event) {
- return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
- }
-
- private void createAndAddDismissView() {
- if (mDismissView != null) {
- mParent.removeView(mDismissView);
- }
- mDismissView = new DismissView(mContext);
- mDismissView.setTargetSizeResId(R.dimen.floating_task_dismiss_circle_size);
- mDismissView.updateResources();
- mParent.addView(mDismissView);
-
- final float dismissRadius = mDismissSize;
- // Save the MagneticTarget instance for the newly set up view - we'll add this to the
- // MagnetizedObjects when the dismiss view gets shown.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(
- mDismissView.getCircle(), (int) dismissRadius);
- }
-
- private MagnetizedObject getMagnetizedView(View v) {
- if (mMagnetizedObject != null
- && Objects.equals(mMagnetizedObject.getUnderlyingObject(), v)) {
- // Same view being dragged, we can reuse the magnetic object.
- return mMagnetizedObject;
- }
- MagnetizedObject magnetizedView = new MagnetizedObject(
- mContext,
- v,
- DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y
- ) {
- @Override
- public float getWidth(@NonNull View underlyingObject) {
- return underlyingObject.getWidth();
- }
-
- @Override
- public float getHeight(@NonNull View underlyingObject) {
- return underlyingObject.getHeight();
- }
-
- @Override
- public void getLocationOnScreen(@NonNull View underlyingObject,
- @NonNull int[] loc) {
- loc[0] = (int) underlyingObject.getTranslationX();
- loc[1] = (int) underlyingObject.getTranslationY();
- }
- };
- magnetizedView.setHapticsEnabled(true);
- magnetizedView.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
- magnetizedView.setStickToTargetMaxXVelocity(STICK_TO_TARGET_MAX_X_VELOCITY);
- magnetizedView.setFlingToTargetWidthPercent(FLING_TO_TARGET_WIDTH_PERCENT);
- return magnetizedView;
- }
-
- /** Animates the dismiss treatment on the view being dismissed. */
- private void animateDismissing(boolean shouldDismiss) {
- if (mViewBeingDismissed == null) {
- return;
- }
- if (shouldDismiss) {
- mDismissAnimator.removeAllListeners();
- mDismissAnimator.start();
- } else {
- mDismissAnimator.removeAllListeners();
- mDismissAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- resetDismissAnimator();
- }
- });
- mDismissAnimator.reverse();
- }
- }
-
- /** Actually dismisses the view. */
- private void doDismiss() {
- mDismissView.hide();
- mController.removeTask();
- resetDismissAnimator();
- mViewBeingDismissed = null;
- }
-
- private void resetDismissAnimator() {
- mDismissAnimator.removeAllListeners();
- mDismissAnimator.cancel();
- if (mDismissView != null) {
- mDismissView.cancelAnimators();
- mDismissView.getCircle().setScaleX(1f);
- mDismissView.getCircle().setScaleY(1f);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java
deleted file mode 100644
index f86d467360f9b..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.wm.shell.floating;
-
-import android.content.Intent;
-
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-/**
- * Interface to interact with floating tasks.
- */
-@ExternalThread
-public interface FloatingTasks {
-
- /**
- * Shows, stashes, or un-stashes the floating task depending on state:
- * - If there is no floating task for this intent, it shows the task for the provided intent.
- * - If there is a floating task for this intent, but it's stashed, this un-stashes it.
- * - If there is a floating task for this intent, and it's not stashed, this stashes it.
- */
- void showOrSetStashed(Intent intent);
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java
deleted file mode 100644
index b3c09d32055bd..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * 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.wm.shell.floating;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.os.SystemProperties;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.bubbles.BubbleController;
-import com.android.wm.shell.common.ExternalInterfaceBinder;
-import com.android.wm.shell.common.RemoteCallable;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellBackgroundThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.floating.views.FloatingTaskView;
-import com.android.wm.shell.sysui.ConfigurationChangeListener;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Entry point for creating and managing floating tasks.
- *
- * A single window layer is added and the task(s) are displayed using a {@link FloatingTaskView}
- * within that window.
- *
- * Currently optimized for a single task. Multiple tasks are not supported.
- */
-public class FloatingTasksController implements RemoteCallable,
- ConfigurationChangeListener {
-
- private static final String TAG = FloatingTasksController.class.getSimpleName();
-
- public static final boolean FLOATING_TASKS_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
- public static final boolean SHOW_FLOATING_TASKS_AS_BUBBLES =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks_as_bubbles", false);
-
- @VisibleForTesting
- static final int SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET = 600;
-
- // Only used for testing
- private Configuration mConfig;
- private boolean mFloatingTasksEnabledForTests;
-
- private FloatingTaskImpl mImpl = new FloatingTaskImpl();
- private Context mContext;
- private ShellController mShellController;
- private ShellCommandHandler mShellCommandHandler;
- private @Nullable BubbleController mBubbleController;
- private WindowManager mWindowManager;
- private ShellTaskOrganizer mTaskOrganizer;
- private TaskViewTransitions mTaskViewTransitions;
- private @ShellMainThread ShellExecutor mMainExecutor;
- // TODO: mBackgroundThread is not used but we'll probs need it eventually?
- private @ShellBackgroundThread ShellExecutor mBackgroundThread;
- private SyncTransactionQueue mSyncQueue;
-
- private boolean mIsFloatingLayerAdded;
- private FloatingTaskLayer mFloatingTaskLayer;
- private final Point mLastPosition = new Point(-1, -1);
-
- private Task mTask;
-
- // Simple class to hold onto info for intent or shortcut based tasks.
- public static class Task {
- public int taskId = INVALID_TASK_ID;
- @Nullable
- public Intent intent;
- @Nullable
- public ShortcutInfo info;
- @Nullable
- public FloatingTaskView floatingView;
- }
-
- public FloatingTasksController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellCommandHandler shellCommandHandler,
- Optional bubbleController,
- WindowManager windowManager,
- ShellTaskOrganizer organizer,
- TaskViewTransitions transitions,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellBackgroundThread ShellExecutor bgExceutor,
- SyncTransactionQueue syncTransactionQueue) {
- mContext = context;
- mShellController = shellController;
- mShellCommandHandler = shellCommandHandler;
- mBubbleController = bubbleController.get();
- mWindowManager = windowManager;
- mTaskOrganizer = organizer;
- mTaskViewTransitions = transitions;
- mMainExecutor = mainExecutor;
- mBackgroundThread = bgExceutor;
- mSyncQueue = syncTransactionQueue;
- if (isFloatingTasksEnabled()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- protected void onInit() {
- mShellController.addConfigurationChangeListener(this);
- mShellController.addExternalInterface(KEY_EXTRA_SHELL_FLOATING_TASKS,
- this::createExternalInterface, this);
- mShellCommandHandler.addDumpCallback(this::dump, this);
- }
-
- /** Only used for testing. */
- @VisibleForTesting
- void setConfig(Configuration config) {
- mConfig = config;
- }
-
- /** Only used for testing. */
- @VisibleForTesting
- void setFloatingTasksEnabled(boolean enabled) {
- mFloatingTasksEnabledForTests = enabled;
- }
-
- /** Whether the floating layer is available. */
- boolean isFloatingLayerAvailable() {
- Configuration config = mConfig == null
- ? mContext.getResources().getConfiguration()
- : mConfig;
- return config.smallestScreenWidthDp >= SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
- }
-
- /** Whether floating tasks are enabled. */
- boolean isFloatingTasksEnabled() {
- return FLOATING_TASKS_ENABLED || mFloatingTasksEnabledForTests;
- }
-
- private ExternalInterfaceBinder createExternalInterface() {
- return new IFloatingTasksImpl(this);
- }
-
- @Override
- public void onThemeChanged() {
- if (mIsFloatingLayerAdded) {
- mFloatingTaskLayer.updateSizes();
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // TODO: probably other stuff here to do (e.g. handle rotation)
- if (mIsFloatingLayerAdded) {
- mFloatingTaskLayer.updateSizes();
- }
- }
-
- /** Returns false if the task shouldn't be shown. */
- private boolean canShowTask(Intent intent) {
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "canShowTask -- %s", intent);
- if (!isFloatingTasksEnabled() || !isFloatingLayerAvailable()) return false;
- if (intent == null) {
- ProtoLog.e(WM_SHELL_FLOATING_APPS, "canShowTask given null intent, doing nothing");
- return false;
- }
- return true;
- }
-
- /** Returns true if the task was or should be shown as a bubble. */
- private boolean maybeShowTaskAsBubble(Intent intent) {
- if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubbleController != null) {
- removeFloatingLayer();
- if (intent.getPackage() != null) {
- mBubbleController.addAppBubble(intent);
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "showing floating task as bubble: %s", intent);
- } else {
- ProtoLog.d(WM_SHELL_FLOATING_APPS,
- "failed to show floating task as bubble: %s; unknown package", intent);
- }
- return true;
- }
- return false;
- }
-
- /**
- * Shows, stashes, or un-stashes the floating task depending on state:
- * - If there is no floating task for this intent, it shows this the provided task.
- * - If there is a floating task for this intent, but it's stashed, this un-stashes it.
- * - If there is a floating task for this intent, and it's not stashed, this stashes it.
- */
- public void showOrSetStashed(Intent intent) {
- if (!canShowTask(intent)) return;
- if (maybeShowTaskAsBubble(intent)) return;
-
- addFloatingLayer();
-
- if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) {
- // The task is already added, toggle the stash state.
- mFloatingTaskLayer.setStashed(mTask, !mTask.floatingView.isStashed());
- return;
- }
-
- // If we're here it's either a new or different task
- showNewTask(intent);
- }
-
- /**
- * Shows a floating task with the provided intent.
- * If the same task is present it will un-stash it or do nothing if it is already un-stashed.
- * Removes any other floating tasks that might exist.
- */
- public void showTask(Intent intent) {
- if (!canShowTask(intent)) return;
- if (maybeShowTaskAsBubble(intent)) return;
-
- addFloatingLayer();
-
- if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) {
- // The task is already added, show it if it's stashed.
- if (mTask.floatingView.isStashed()) {
- mFloatingTaskLayer.setStashed(mTask, false);
- }
- return;
- }
- showNewTask(intent);
- }
-
- private void showNewTask(Intent intent) {
- if (mTask != null && !intent.filterEquals(mTask.intent)) {
- mFloatingTaskLayer.removeAllTaskViews();
- mTask.floatingView.cleanUpTaskView();
- mTask = null;
- }
-
- FloatingTaskView ftv = new FloatingTaskView(mContext, this);
- ftv.createTaskView(mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
-
- mTask = new Task();
- mTask.floatingView = ftv;
- mTask.intent = intent;
-
- // Add & start the task.
- mFloatingTaskLayer.addTask(mTask);
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "showNewTask, startingIntent: %s", intent);
- mTask.floatingView.startTask(mMainExecutor, mTask);
- }
-
- /**
- * Removes the task and cleans up the view.
- */
- public void removeTask() {
- if (mTask != null) {
- ProtoLog.d(WM_SHELL_FLOATING_APPS, "Removing task with id=%d", mTask.taskId);
-
- if (mTask.floatingView != null) {
- // TODO: animate it
- mFloatingTaskLayer.removeView(mTask.floatingView);
- mTask.floatingView.cleanUpTaskView();
- }
- removeFloatingLayer();
- }
- }
-
- /**
- * Whether there is a floating task and if it is stashed.
- */
- public boolean isStashed() {
- return isTaskAttached(mTask) && mTask.floatingView.isStashed();
- }
-
- /**
- * If a floating task exists, this sets whether it is stashed and animates if needed.
- */
- public void setStashed(boolean shouldStash) {
- if (mTask != null && mTask.floatingView != null && mIsFloatingLayerAdded) {
- mFloatingTaskLayer.setStashed(mTask, shouldStash);
- }
- }
-
- /**
- * Saves the last position the floating task was in so that it can be put there again.
- */
- public void setLastPosition(int x, int y) {
- mLastPosition.set(x, y);
- }
-
- /**
- * Returns the last position the floating task was in.
- */
- public Point getLastPosition() {
- return mLastPosition;
- }
-
- /**
- * Whether the provided task has a view that's attached to the floating layer.
- */
- private boolean isTaskAttached(Task t) {
- return t != null && t.floatingView != null
- && mIsFloatingLayerAdded
- && mFloatingTaskLayer.getTaskViewCount() > 0
- && Objects.equals(mFloatingTaskLayer.getFirstTaskView(), t.floatingView);
- }
-
- // TODO: when this is added, if there are bubbles, they get hidden? Is only one layer of this
- // type allowed? Bubbles & floating tasks should probably be in the same layer to reduce
- // # of windows.
- private void addFloatingLayer() {
- if (mIsFloatingLayerAdded) {
- return;
- }
-
- mFloatingTaskLayer = new FloatingTaskLayer(mContext, this, mWindowManager);
-
- WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT
- );
- params.setTrustedOverlay();
- params.setFitInsetsTypes(0);
- params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
- params.setTitle("FloatingTaskLayer");
- params.packageName = mContext.getPackageName();
- params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
- try {
- mIsFloatingLayerAdded = true;
- mWindowManager.addView(mFloatingTaskLayer, params);
- } catch (IllegalStateException e) {
- // This means the floating layer has already been added which shouldn't happen.
- e.printStackTrace();
- }
- }
-
- private void removeFloatingLayer() {
- if (!mIsFloatingLayerAdded) {
- return;
- }
- try {
- mIsFloatingLayerAdded = false;
- if (mFloatingTaskLayer != null) {
- mWindowManager.removeView(mFloatingTaskLayer);
- }
- } catch (IllegalArgumentException e) {
- // This means the floating layer has already been removed which shouldn't happen.
- e.printStackTrace();
- }
- }
-
- /**
- * Description of current floating task state.
- */
- private void dump(PrintWriter pw, String prefix) {
- pw.println("FloatingTaskController state:");
- pw.print(" isFloatingLayerAvailable= "); pw.println(isFloatingLayerAvailable());
- pw.print(" isFloatingTasksEnabled= "); pw.println(isFloatingTasksEnabled());
- pw.print(" mIsFloatingLayerAdded= "); pw.println(mIsFloatingLayerAdded);
- pw.print(" mLastPosition= "); pw.println(mLastPosition);
- pw.println();
- }
-
- /** Returns the {@link FloatingTasks} implementation. */
- public FloatingTasks asFloatingTasks() {
- return mImpl;
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
-
- @Override
- public ShellExecutor getRemoteCallExecutor() {
- return mMainExecutor;
- }
-
- /**
- * The interface for calls from outside the shell, within the host process.
- */
- @ExternalThread
- private class FloatingTaskImpl implements FloatingTasks {
- @Override
- public void showOrSetStashed(Intent intent) {
- mMainExecutor.execute(() -> FloatingTasksController.this.showOrSetStashed(intent));
- }
- }
-
- /**
- * The interface for calls from outside the host process.
- */
- @BinderThread
- private static class IFloatingTasksImpl extends IFloatingTasks.Stub
- implements ExternalInterfaceBinder {
- private FloatingTasksController mController;
-
- IFloatingTasksImpl(FloatingTasksController controller) {
- mController = controller;
- }
-
- /**
- * Invalidates this instance, preventing future calls from updating the controller.
- */
- @Override
- public void invalidate() {
- mController = null;
- }
-
- public void showTask(Intent intent) {
- executeRemoteCallWithTaskPermission(mController, "showTask",
- (controller) -> controller.showTask(intent));
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
deleted file mode 100644
index f79ca1039865d..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.wm.shell.floating;
-
-import android.content.Intent;
-
-/**
- * Interface that is exposed to remote callers to manipulate floating task features.
- */
-interface IFloatingTasks {
-
- void showTask(in Intent intent) = 1;
-
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java
deleted file mode 100644
index c922109751ba6..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.wm.shell.floating.views;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.wm.shell.R;
-
-/**
- * Displays the menu items for a floating task view (e.g. close).
- */
-public class FloatingMenuView extends LinearLayout {
-
- private int mItemSize;
- private int mItemMargin;
-
- public FloatingMenuView(Context context) {
- super(context);
- setOrientation(LinearLayout.HORIZONTAL);
- setGravity(Gravity.CENTER);
-
- mItemSize = context.getResources().getDimensionPixelSize(
- R.dimen.floating_task_menu_item_size);
- mItemMargin = context.getResources().getDimensionPixelSize(
- R.dimen.floating_task_menu_item_padding);
- }
-
- /** Adds a clickable item to the menu bar. Items are ordered as added. */
- public void addMenuItem(@Nullable Drawable drawable, View.OnClickListener listener) {
- ImageView itemView = new ImageView(getContext());
- itemView.setScaleType(ImageView.ScaleType.CENTER);
- if (drawable != null) {
- itemView.setImageDrawable(drawable);
- }
- LinearLayout.LayoutParams lp = new LayoutParams(mItemSize,
- ViewGroup.LayoutParams.MATCH_PARENT);
- lp.setMarginStart(mItemMargin);
- lp.setMarginEnd(mItemMargin);
- addView(itemView, lp);
-
- itemView.setOnClickListener(listener);
- }
-
- /**
- * The menu extends past the top of the TaskView because of the rounded corners. This means
- * to center content in the menu we must subtract the radius (i.e. the amount of space covered
- * by TaskView).
- */
- public void setCornerRadius(float radius) {
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java
deleted file mode 100644
index 16dab2415bf2c..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * 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.wm.shell.floating.views;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.FlingAnimation;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.R;
-import com.android.wm.shell.floating.FloatingDismissController;
-import com.android.wm.shell.floating.FloatingTasksController;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This is the layout that {@link FloatingTaskView}s are contained in. It handles input and
- * movement of the task views.
- */
-public class FloatingTaskLayer extends FrameLayout
- implements ViewTreeObserver.OnComputeInternalInsetsListener {
-
- private static final String TAG = FloatingTaskLayer.class.getSimpleName();
-
- /** How big to make the task view based on screen width of the largest size. */
- private static final float START_SIZE_WIDTH_PERCENT = 0.33f;
- /** Min fling velocity required to move the view from one side of the screen to the other. */
- private static final float ESCAPE_VELOCITY = 750f;
- /** Amount of friction to apply to fling animations. */
- private static final float FLING_FRICTION = 1.9f;
-
- private final FloatingTasksController mController;
- private final FloatingDismissController mDismissController;
- private final WindowManager mWindowManager;
- private final TouchHandlerImpl mTouchHandler;
-
- private final Region mTouchableRegion = new Region();
- private final Rect mPositionRect = new Rect();
- private final Point mDefaultStartPosition = new Point();
- private final Point mTaskViewSize = new Point();
- private WindowInsets mWindowInsets;
- private int mVerticalPadding;
- private int mOverhangWhenStashed;
-
- private final List mSystemGestureExclusionRects = Collections.singletonList(new Rect());
- private ViewTreeObserver.OnDrawListener mSystemGestureExclusionListener =
- this::updateSystemGestureExclusion;
-
- /** Interface allowing something to handle the touch events going to a task. */
- interface FloatingTaskTouchHandler {
- void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float viewInitialX, float viewInitialY);
-
- void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy);
-
- void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy, float velX, float velY);
-
- void onClick(@NonNull FloatingTaskView v);
- }
-
- public FloatingTaskLayer(Context context,
- FloatingTasksController controller,
- WindowManager windowManager) {
- super(context);
- // TODO: Why is this necessary? Without it FloatingTaskView does not render correctly.
- setBackgroundColor(Color.argb(0, 0, 0, 0));
-
- mController = controller;
- mWindowManager = windowManager;
- updateSizes();
-
- // TODO: Might make sense to put dismiss controller in the touch handler since that's the
- // main user of dismiss controller.
- mDismissController = new FloatingDismissController(context, mController, this);
- mTouchHandler = new TouchHandlerImpl();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- getViewTreeObserver().addOnDrawListener(mSystemGestureExclusionListener);
- setOnApplyWindowInsetsListener((view, windowInsets) -> {
- if (!windowInsets.equals(mWindowInsets)) {
- mWindowInsets = windowInsets;
- updateSizes();
- }
- return windowInsets;
- });
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- getViewTreeObserver().removeOnDrawListener(mSystemGestureExclusionListener);
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- mTouchableRegion.setEmpty();
- getTouchableRegion(mTouchableRegion);
- inoutInfo.touchableRegion.set(mTouchableRegion);
- }
-
- /** Adds a floating task to the layout. */
- public void addTask(FloatingTasksController.Task task) {
- if (task.floatingView == null) return;
-
- task.floatingView.setTouchHandler(mTouchHandler);
- addView(task.floatingView, new LayoutParams(mTaskViewSize.x, mTaskViewSize.y));
- updateTaskViewPosition(task.floatingView);
- }
-
- /** Animates the stashed state of the provided task, if it's part of the floating layer. */
- public void setStashed(FloatingTasksController.Task task, boolean shouldStash) {
- if (task.floatingView != null && task.floatingView.getParent() == this) {
- mTouchHandler.stashTaskView(task.floatingView, shouldStash);
- }
- }
-
- /** Removes all {@link FloatingTaskView} from the layout. */
- public void removeAllTaskViews() {
- int childCount = getChildCount();
- ArrayList viewsToRemove = new ArrayList<>();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FloatingTaskView) {
- viewsToRemove.add(getChildAt(i));
- }
- }
- for (View v : viewsToRemove) {
- removeView(v);
- }
- }
-
- /** Returns the number of task views in the layout. */
- public int getTaskViewCount() {
- int taskViewCount = 0;
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FloatingTaskView) {
- taskViewCount++;
- }
- }
- return taskViewCount;
- }
-
- /**
- * Called when the task view is un-stuck from the dismiss target.
- * @param v the task view being moved.
- * @param velX the x velocity of the motion event.
- * @param velY the y velocity of the motion event.
- * @param wasFlungOut true if the user flung the task view out of the dismiss target (i.e. there
- * was an 'up' event), otherwise the user is still dragging.
- */
- public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY,
- boolean wasFlungOut) {
- mTouchHandler.onUnstuckFromTarget(v, velX, velY, wasFlungOut);
- }
-
- /**
- * Updates dimensions and applies them to any task views.
- */
- public void updateSizes() {
- if (mDismissController != null) {
- mDismissController.updateSizes();
- }
-
- mOverhangWhenStashed = getResources().getDimensionPixelSize(
- R.dimen.floating_task_stash_offset);
- mVerticalPadding = getResources().getDimensionPixelSize(
- R.dimen.floating_task_vertical_padding);
-
- WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
- WindowInsets windowInsets = windowMetrics.getWindowInsets();
- Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
- | WindowInsets.Type.statusBars()
- | WindowInsets.Type.displayCutout());
- Rect bounds = windowMetrics.getBounds();
- mPositionRect.set(bounds.left + insets.left,
- bounds.top + insets.top + mVerticalPadding,
- bounds.right - insets.right,
- bounds.bottom - insets.bottom - mVerticalPadding);
-
- int taskViewWidth = Math.max(bounds.height(), bounds.width());
- int taskViewHeight = Math.min(bounds.height(), bounds.width());
- taskViewHeight = taskViewHeight - (insets.top + insets.bottom + (mVerticalPadding * 2));
- mTaskViewSize.set((int) (taskViewWidth * START_SIZE_WIDTH_PERCENT), taskViewHeight);
- mDefaultStartPosition.set(mPositionRect.left, mPositionRect.top);
-
- // Update existing views
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FloatingTaskView) {
- FloatingTaskView child = (FloatingTaskView) getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.width = mTaskViewSize.x;
- lp.height = mTaskViewSize.y;
- child.setLayoutParams(lp);
- updateTaskViewPosition(child);
- }
- }
- }
-
- /** Returns the first floating task view in the layout. (Currently only ever 1 view). */
- @Nullable
- public FloatingTaskView getFirstTaskView() {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child instanceof FloatingTaskView) {
- return (FloatingTaskView) child;
- }
- }
- return null;
- }
-
- private void updateTaskViewPosition(FloatingTaskView floatingView) {
- Point lastPosition = mController.getLastPosition();
- if (lastPosition.x == -1 && lastPosition.y == -1) {
- floatingView.setX(mDefaultStartPosition.x);
- floatingView.setY(mDefaultStartPosition.y);
- } else {
- floatingView.setX(lastPosition.x);
- floatingView.setY(lastPosition.y);
- }
- if (mTouchHandler.isStashedPosition(floatingView)) {
- floatingView.setStashed(true);
- }
- floatingView.updateLocation();
- }
-
- /**
- * Updates the area of the screen that shouldn't allow the back gesture due to the placement
- * of task view (i.e. when task view is stashed on an edge, tapping or swiping that edge would
- * un-stash the task view instead of performing the back gesture).
- */
- private void updateSystemGestureExclusion() {
- Rect excludeZone = mSystemGestureExclusionRects.get(0);
- FloatingTaskView floatingTaskView = getFirstTaskView();
- if (floatingTaskView != null && floatingTaskView.isStashed()) {
- excludeZone.set(floatingTaskView.getLeft(),
- floatingTaskView.getTop(),
- floatingTaskView.getRight(),
- floatingTaskView.getBottom());
- excludeZone.offset((int) (floatingTaskView.getTranslationX()),
- (int) (floatingTaskView.getTranslationY()));
- setSystemGestureExclusionRects(mSystemGestureExclusionRects);
- } else {
- excludeZone.setEmpty();
- setSystemGestureExclusionRects(Collections.emptyList());
- }
- }
-
- /**
- * Fills in the touchable region for floating windows. This is used by WindowManager to
- * decide which touch events go to the floating windows.
- */
- private void getTouchableRegion(Region outRegion) {
- int childCount = getChildCount();
- Rect temp = new Rect();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child instanceof FloatingTaskView) {
- child.getBoundsOnScreen(temp);
- outRegion.op(temp, Region.Op.UNION);
- }
- }
- }
-
- /**
- * Implementation of the touch handler. Animates the task view based on touch events.
- */
- private class TouchHandlerImpl implements FloatingTaskTouchHandler {
- /**
- * The view can be stashed by swiping it towards the current edge or moving it there. If
- * the view gets moved in a way that is not one of these gestures, this is flipped to false.
- */
- private boolean mCanStash = true;
- /**
- * This is used to indicate that the view has been un-stuck from the dismiss target and
- * needs to spring to the current touch location.
- */
- // TODO: implement this behavior
- private boolean mSpringToTouchOnNextMotionEvent = false;
-
- private ArrayList mFlingAnimations;
- private ViewPropertyAnimator mViewPropertyAnimation;
-
- private float mViewInitialX;
- private float mViewInitialY;
-
- private float[] mMinMax = new float[2];
-
- @Override
- public void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, float viewInitialX,
- float viewInitialY) {
- mCanStash = true;
- mViewInitialX = viewInitialX;
- mViewInitialY = viewInitialY;
- mDismissController.setUpMagneticObject(v);
- mDismissController.passEventToMagnetizedObject(ev);
- }
-
- @Override
- public void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy) {
- // Shows the magnetic dismiss target if needed.
- mDismissController.showDismiss(/* show= */ true);
-
- // Send it to magnetic target first.
- if (mDismissController.passEventToMagnetizedObject(ev)) {
- v.setStashed(false);
- mCanStash = true;
-
- return;
- }
-
- // If we're here magnetic target didn't want it so move as per normal.
-
- v.setTranslationX(capX(v, mViewInitialX + dx, /* isMoving= */ true));
- v.setTranslationY(capY(v, mViewInitialY + dy));
- if (v.isStashed()) {
- // Check if we've moved far enough to be not stashed.
- final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
- final boolean viewInitiallyOnLeftSide = mViewInitialX < centerX;
- if (viewInitiallyOnLeftSide) {
- if (v.getTranslationX() > mPositionRect.left) {
- v.setStashed(false);
- mCanStash = true;
- }
- } else if (v.getTranslationX() + v.getWidth() < mPositionRect.right) {
- v.setStashed(false);
- mCanStash = true;
- }
- }
- }
-
- // Reference for math / values: StackAnimationController#flingStackThenSpringToEdge.
- // TODO clean up the code here, pretty hard to comprehend
- // TODO code here doesn't work the best when in portrait (e.g. can't fling up/down on edges)
- @Override
- public void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
- float dx, float dy, float velX, float velY) {
-
- // Send it to magnetic target first.
- if (mDismissController.passEventToMagnetizedObject(ev)) {
- v.setStashed(false);
- return;
- }
- mDismissController.showDismiss(/* show= */ false);
-
- // If we're here magnetic target didn't want it so handle up as per normal.
-
- final float x = capX(v, mViewInitialX + dx, /* isMoving= */ false);
- final float centerX = mPositionRect.centerX();
- final boolean viewInitiallyOnLeftSide = mViewInitialX + v.getWidth() < centerX;
- final boolean viewOnLeftSide = x + v.getWidth() < centerX;
- final boolean isFling = Math.abs(velX) > ESCAPE_VELOCITY;
- final boolean isFlingLeft = isFling && velX < ESCAPE_VELOCITY;
- // TODO: check velX here sometimes it doesn't stash on move when I think it should
- final boolean shouldStashFromMove =
- (velX < 0 && v.getTranslationX() < mPositionRect.left)
- || (velX > 0
- && v.getTranslationX() + v.getWidth() > mPositionRect.right);
- final boolean shouldStashFromFling = viewInitiallyOnLeftSide == viewOnLeftSide
- && isFling
- && ((viewOnLeftSide && velX < ESCAPE_VELOCITY)
- || (!viewOnLeftSide && velX > ESCAPE_VELOCITY));
- final boolean shouldStash = mCanStash && (shouldStashFromFling || shouldStashFromMove);
-
- ProtoLog.d(WM_SHELL_FLOATING_APPS,
- "shouldStash=%s shouldStashFromFling=%s shouldStashFromMove=%s"
- + " viewInitiallyOnLeftSide=%s viewOnLeftSide=%s isFling=%s velX=%f"
- + " isStashed=%s", shouldStash, shouldStashFromFling, shouldStashFromMove,
- viewInitiallyOnLeftSide, viewOnLeftSide, isFling, velX, v.isStashed());
-
- if (v.isStashed()) {
- mMinMax[0] = viewOnLeftSide
- ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed
- : mPositionRect.right - v.getWidth();
- mMinMax[1] = viewOnLeftSide
- ? mPositionRect.left
- : mPositionRect.right - mOverhangWhenStashed;
- } else {
- populateMinMax(v, viewOnLeftSide, shouldStash, mMinMax);
- }
-
- boolean movingLeft = isFling ? isFlingLeft : viewOnLeftSide;
- float destinationRelativeX = movingLeft
- ? mMinMax[0]
- : mMinMax[1];
-
- // TODO: why is this necessary / when does this happen?
- if (mMinMax[1] < v.getTranslationX()) {
- mMinMax[1] = v.getTranslationX();
- }
- if (v.getTranslationX() < mMinMax[0]) {
- mMinMax[0] = v.getTranslationX();
- }
-
- // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity
- // so that it'll make it all the way to the side of the screen.
- final float minimumVelocityToReachEdge =
- getMinimumVelocityToReachEdge(v, destinationRelativeX);
- final float startXVelocity = movingLeft
- ? Math.min(minimumVelocityToReachEdge, velX)
- : Math.max(minimumVelocityToReachEdge, velX);
-
- cancelAnyAnimations(v);
-
- mFlingAnimations = getAnimationForUpEvent(v, shouldStash,
- startXVelocity, mMinMax[0], mMinMax[1], destinationRelativeX);
- for (int i = 0; i < mFlingAnimations.size(); i++) {
- mFlingAnimations.get(i).start();
- }
- }
-
- @Override
- public void onClick(@NonNull FloatingTaskView v) {
- if (v.isStashed()) {
- final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
- final boolean viewOnLeftSide = v.getTranslationX() < centerX;
- final float destinationRelativeX = viewOnLeftSide
- ? mPositionRect.left
- : mPositionRect.right - v.getWidth();
- final float minimumVelocityToReachEdge =
- getMinimumVelocityToReachEdge(v, destinationRelativeX);
- populateMinMax(v, viewOnLeftSide, /* stashed= */ true, mMinMax);
-
- cancelAnyAnimations(v);
-
- FlingAnimation flingAnimation = new FlingAnimation(v,
- DynamicAnimation.TRANSLATION_X);
- flingAnimation.setFriction(FLING_FRICTION)
- .setStartVelocity(minimumVelocityToReachEdge)
- .setMinValue(mMinMax[0])
- .setMaxValue(mMinMax[1])
- .addEndListener((animation, canceled, value, velocity) -> {
- if (canceled) return;
- mController.setLastPosition((int) v.getTranslationX(),
- (int) v.getTranslationY());
- v.setStashed(false);
- v.updateLocation();
- });
- mFlingAnimations = new ArrayList<>();
- mFlingAnimations.add(flingAnimation);
- flingAnimation.start();
- }
- }
-
- public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY,
- boolean wasFlungOut) {
- if (wasFlungOut) {
- snapTaskViewToEdge(v, velX, /* shouldStash= */ false);
- } else {
- // TODO: use this for something / to spring the view to the touch location
- mSpringToTouchOnNextMotionEvent = true;
- }
- }
-
- public void stashTaskView(FloatingTaskView v, boolean shouldStash) {
- if (v.isStashed() == shouldStash) {
- return;
- }
- final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
- final boolean viewOnLeftSide = v.getTranslationX() < centerX;
- snapTaskViewToEdge(v, viewOnLeftSide ? -ESCAPE_VELOCITY : ESCAPE_VELOCITY, shouldStash);
- }
-
- public boolean isStashedPosition(View v) {
- return v.getTranslationX() < mPositionRect.left
- || v.getTranslationX() + v.getWidth() > mPositionRect.right;
- }
-
- // TODO: a lot of this is duplicated in onUp -- can it be unified?
- private void snapTaskViewToEdge(FloatingTaskView v, float velX, boolean shouldStash) {
- final boolean movingLeft = velX < ESCAPE_VELOCITY;
- populateMinMax(v, movingLeft, shouldStash, mMinMax);
- float destinationRelativeX = movingLeft
- ? mMinMax[0]
- : mMinMax[1];
-
- // TODO: why is this necessary / when does this happen?
- if (mMinMax[1] < v.getTranslationX()) {
- mMinMax[1] = v.getTranslationX();
- }
- if (v.getTranslationX() < mMinMax[0]) {
- mMinMax[0] = v.getTranslationX();
- }
-
- // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity
- // so that it'll make it all the way to the side of the screen.
- final float minimumVelocityToReachEdge =
- getMinimumVelocityToReachEdge(v, destinationRelativeX);
- final float startXVelocity = movingLeft
- ? Math.min(minimumVelocityToReachEdge, velX)
- : Math.max(minimumVelocityToReachEdge, velX);
-
- cancelAnyAnimations(v);
-
- mFlingAnimations = getAnimationForUpEvent(v,
- shouldStash, startXVelocity, mMinMax[0], mMinMax[1],
- destinationRelativeX);
- for (int i = 0; i < mFlingAnimations.size(); i++) {
- mFlingAnimations.get(i).start();
- }
- }
-
- private void cancelAnyAnimations(FloatingTaskView v) {
- if (mFlingAnimations != null) {
- for (int i = 0; i < mFlingAnimations.size(); i++) {
- if (mFlingAnimations.get(i).isRunning()) {
- mFlingAnimations.get(i).cancel();
- }
- }
- }
- if (mViewPropertyAnimation != null) {
- mViewPropertyAnimation.cancel();
- mViewPropertyAnimation = null;
- }
- }
-
- private ArrayList getAnimationForUpEvent(FloatingTaskView v,
- boolean shouldStash, float startVelX, float minValue, float maxValue,
- float destinationRelativeX) {
- final float ty = v.getTranslationY();
- final ArrayList animations = new ArrayList<>();
- if (ty != capY(v, ty)) {
- // The view was being dismissed so the Y is out of bounds, need to animate that.
- FlingAnimation yFlingAnimation = new FlingAnimation(v,
- DynamicAnimation.TRANSLATION_Y);
- yFlingAnimation.setFriction(FLING_FRICTION)
- .setStartVelocity(startVelX)
- .setMinValue(mPositionRect.top)
- .setMaxValue(mPositionRect.bottom - mTaskViewSize.y);
- animations.add(yFlingAnimation);
- }
- FlingAnimation flingAnimation = new FlingAnimation(v, DynamicAnimation.TRANSLATION_X);
- flingAnimation.setFriction(FLING_FRICTION)
- .setStartVelocity(startVelX)
- .setMinValue(minValue)
- .setMaxValue(maxValue)
- .addEndListener((animation, canceled, value, velocity) -> {
- if (canceled) return;
- Runnable endAction = () -> {
- v.setStashed(shouldStash);
- v.updateLocation();
- if (!v.isStashed()) {
- mController.setLastPosition((int) v.getTranslationX(),
- (int) v.getTranslationY());
- }
- };
- if (!shouldStash) {
- final int xTranslation = (int) v.getTranslationX();
- if (xTranslation != destinationRelativeX) {
- // TODO: this animation doesn't feel great, should figure out
- // a better way to do this or remove the need for it all together.
- mViewPropertyAnimation = v.animate()
- .translationX(destinationRelativeX)
- .setListener(getAnimationListener(endAction));
- mViewPropertyAnimation.start();
- } else {
- endAction.run();
- }
- } else {
- endAction.run();
- }
- });
- animations.add(flingAnimation);
- return animations;
- }
-
- private AnimatorListenerAdapter getAnimationListener(Runnable endAction) {
- return new AnimatorListenerAdapter() {
- boolean translationCanceled = false;
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- translationCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- if (!translationCanceled) {
- endAction.run();
- }
- }
- };
- }
-
- private void populateMinMax(FloatingTaskView v, boolean onLeft, boolean shouldStash,
- float[] out) {
- if (shouldStash) {
- out[0] = onLeft
- ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed
- : mPositionRect.right - v.getWidth();
- out[1] = onLeft
- ? mPositionRect.left
- : mPositionRect.right - mOverhangWhenStashed;
- } else {
- out[0] = mPositionRect.left;
- out[1] = mPositionRect.right - mTaskViewSize.x;
- }
- }
-
- private float getMinimumVelocityToReachEdge(FloatingTaskView v,
- float destinationRelativeX) {
- // Minimum velocity required for the view to make it to the targeted side of the screen,
- // taking friction into account (4.2f is the number that friction scalars are multiplied
- // by in DynamicAnimation.DragForce). This is an estimate and could be slightly off, the
- // animation at the end will ensure that it reaches the destination X regardless.
- return (destinationRelativeX - v.getTranslationX()) * (FLING_FRICTION * 4.2f);
- }
-
- private float capX(FloatingTaskView v, float x, boolean isMoving) {
- final int width = v.getWidth();
- if (v.isStashed() || isMoving) {
- if (x < mPositionRect.left - v.getWidth() + mOverhangWhenStashed) {
- return mPositionRect.left - v.getWidth() + mOverhangWhenStashed;
- }
- if (x > mPositionRect.right - mOverhangWhenStashed) {
- return mPositionRect.right - mOverhangWhenStashed;
- }
- } else {
- if (x < mPositionRect.left) {
- return mPositionRect.left;
- }
- if (x > mPositionRect.right - width) {
- return mPositionRect.right - width;
- }
- }
- return x;
- }
-
- private float capY(FloatingTaskView v, float y) {
- final int height = v.getHeight();
- if (y < mPositionRect.top) {
- return mPositionRect.top;
- }
- if (y > mPositionRect.bottom - height) {
- return mPositionRect.bottom - height;
- }
- return y;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java
deleted file mode 100644
index 581204a82ec7e..0000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * 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.wm.shell.floating.views;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.TaskView;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.bubbles.RelativeTouchListener;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.floating.FloatingTasksController;
-
-/**
- * A view that holds a floating task using {@link TaskView} along with additional UI to manage
- * the task.
- */
-public class FloatingTaskView extends FrameLayout {
-
- private static final String TAG = FloatingTaskView.class.getSimpleName();
-
- private FloatingTasksController mController;
-
- private FloatingMenuView mMenuView;
- private int mMenuHeight;
- private TaskView mTaskView;
-
- private float mCornerRadius = 0f;
- private int mBackgroundColor;
-
- private FloatingTasksController.Task mTask;
-
- private boolean mIsStashed;
-
- /**
- * Creates a floating task view.
- *
- * @param context the context to use.
- * @param controller the controller to notify about changes in the floating task (e.g. removal).
- */
- public FloatingTaskView(Context context, FloatingTasksController controller) {
- super(context);
- mController = controller;
- setElevation(getResources().getDimensionPixelSize(R.dimen.floating_task_elevation));
- mMenuHeight = context.getResources().getDimensionPixelSize(R.dimen.floating_task_menu_size);
- mMenuView = new FloatingMenuView(context);
- addView(mMenuView);
-
- applyThemeAttrs();
-
- setClipToOutline(true);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
- }
- });
- }
-
- // TODO: call this when theme/config changes
- void applyThemeAttrs() {
- boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
- mContext.getResources());
- final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
- android.R.attr.dialogCornerRadius,
- android.R.attr.colorBackgroundFloating});
- mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
- mCornerRadius = mCornerRadius / 2f;
- mBackgroundColor = ta.getColor(1, Color.WHITE);
-
- ta.recycle();
-
- mMenuView.setCornerRadius(mCornerRadius);
- mMenuHeight = getResources().getDimensionPixelSize(
- R.dimen.floating_task_menu_size);
-
- if (mTaskView != null) {
- mTaskView.setCornerRadius(mCornerRadius);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // Add corner radius here so that the menu extends behind the rounded corners of TaskView.
- int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height);
- measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
-
- if (mTaskView != null) {
- int taskViewHeight = height - menuViewHeight;
- measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(taskViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // Drag handle above
- final int dragHandleBottom = t + mMenuView.getMeasuredHeight();
- mMenuView.layout(l, t, r, dragHandleBottom);
- if (mTaskView != null) {
- // Subtract radius so that the menu extends behind the rounded corners of TaskView.
- mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r,
- dragHandleBottom + mTaskView.getMeasuredHeight());
- }
- }
-
- /**
- * Constructs the TaskView to display the task. Must be called for {@link #startTask} to work.
- */
- public void createTaskView(Context context, ShellTaskOrganizer organizer,
- TaskViewTransitions transitions, SyncTransactionQueue syncQueue) {
- mTaskView = new TaskView(context, organizer, transitions, syncQueue);
- addView(mTaskView);
- mTaskView.setEnableSurfaceClipping(true);
- mTaskView.setCornerRadius(mCornerRadius);
- }
-
- /**
- * Starts the provided task in the TaskView, if the TaskView exists. This should be called after
- * {@link #createTaskView}.
- */
- public void startTask(@ShellMainThread ShellExecutor executor,
- FloatingTasksController.Task task) {
- if (mTaskView == null) {
- Log.e(TAG, "starting task before creating the view!");
- return;
- }
- mTask = task;
- mTaskView.setListener(executor, mTaskViewListener);
- }
-
- /**
- * Sets the touch handler for the view.
- *
- * @param handler the touch handler for the view.
- */
- public void setTouchHandler(FloatingTaskLayer.FloatingTaskTouchHandler handler) {
- setOnTouchListener(new RelativeTouchListener() {
- @Override
- public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
- handler.onDown(FloatingTaskView.this, ev, v.getTranslationX(), v.getTranslationY());
- return true;
- }
-
- @Override
- public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
- float viewInitialY, float dx, float dy) {
- handler.onMove(FloatingTaskView.this, ev, dx, dy);
- }
-
- @Override
- public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
- float viewInitialY, float dx, float dy, float velX, float velY) {
- handler.onUp(FloatingTaskView.this, ev, dx, dy, velX, velY);
- }
- });
- setOnClickListener(view -> {
- handler.onClick(FloatingTaskView.this);
- });
-
- mMenuView.addMenuItem(null, view -> {
- if (mIsStashed) {
- // If we're stashed all clicks un-stash.
- handler.onClick(FloatingTaskView.this);
- }
- });
- }
-
- private void setContentVisibility(boolean visible) {
- if (mTaskView == null) return;
- mTaskView.setAlpha(visible ? 1f : 0f);
- }
-
- /**
- * Sets the alpha of both this view and the TaskView.
- */
- public void setTaskViewAlpha(float alpha) {
- if (mTaskView != null) {
- mTaskView.setAlpha(alpha);
- }
- setAlpha(alpha);
- }
-
- /**
- * Call when the location or size of the view has changed to update TaskView.
- */
- public void updateLocation() {
- if (mTaskView == null) return;
- mTaskView.onLocationChanged();
- }
-
- private void updateMenuColor() {
- ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo();
- int color = info != null ? info.taskDescription.getBackgroundColor() : -1;
- if (color != -1) {
- mMenuView.setBackgroundColor(color);
- } else {
- mMenuView.setBackgroundColor(mBackgroundColor);
- }
- }
-
- /**
- * Sets whether the view is stashed or not.
- *
- * Also updates the touchable area based on this. If the view is stashed we don't direct taps
- * on the activity to the activity, instead a tap will un-stash the view.
- */
- public void setStashed(boolean isStashed) {
- if (mIsStashed != isStashed) {
- mIsStashed = isStashed;
- if (mTaskView == null) {
- return;
- }
- updateObscuredTouchRect();
- }
- }
-
- /** Whether the view is stashed at the edge of the screen or not. **/
- public boolean isStashed() {
- return mIsStashed;
- }
-
- private void updateObscuredTouchRect() {
- if (mIsStashed) {
- Rect tmpRect = new Rect();
- getBoundsOnScreen(tmpRect);
- mTaskView.setObscuredTouchRect(tmpRect);
- } else {
- mTaskView.setObscuredTouchRect(null);
- }
- }
-
- /**
- * Whether the task needs to be restarted, this can happen when {@link #cleanUpTaskView()} has
- * been called on this view or if
- * {@link #startTask(ShellExecutor, FloatingTasksController.Task)} was never called.
- */
- public boolean needsTaskStarted() {
- // If the task needs to be restarted then TaskView would have been cleaned up.
- return mTaskView == null;
- }
-
- /** Call this when the floating task activity is no longer in use. */
- public void cleanUpTaskView() {
- if (mTask != null && mTask.taskId != INVALID_TASK_ID) {
- try {
- ActivityTaskManager.getService().removeTask(mTask.taskId);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
- }
- }
- if (mTaskView != null) {
- mTaskView.release();
- removeView(mTaskView);
- mTaskView = null;
- }
- }
-
- // TODO: use task background colour / how to get the taskInfo ?
- private static int getDragBarColor(ActivityManager.RunningTaskInfo taskInfo) {
- final int taskBgColor = taskInfo.taskDescription.getStatusBarColor();
- return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
- }
-
- private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
- private boolean mInitialized = false;
- private boolean mDestroyed = false;
-
- @Override
- public void onInitialized() {
- if (mDestroyed || mInitialized) {
- return;
- }
- // Custom options so there is no activity transition animation
- ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
- /* enterResId= */ 0, /* exitResId= */ 0);
-
- Rect launchBounds = new Rect();
- mTaskView.getBoundsOnScreen(launchBounds);
-
- try {
- options.setTaskAlwaysOnTop(true);
- if (mTask.intent != null) {
- Intent fillInIntent = new Intent();
- // Apply flags to make behaviour match documentLaunchMode=always.
- fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, mTask.intent,
- PendingIntent.FLAG_MUTABLE,
- null);
- mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
- } else {
- ProtoLog.e(WM_SHELL_FLOATING_APPS, "Tried to start a task with null intent");
- }
- } catch (RuntimeException e) {
- ProtoLog.e(WM_SHELL_FLOATING_APPS, "Exception while starting task: %s",
- e.getMessage());
- mController.removeTask();
- }
- mInitialized = true;
- }
-
- @Override
- public void onReleased() {
- mDestroyed = true;
- }
-
- @Override
- public void onTaskCreated(int taskId, ComponentName name) {
- mTask.taskId = taskId;
- updateMenuColor();
- setContentVisibility(true);
- }
-
- @Override
- public void onTaskVisibilityChanged(int taskId, boolean visible) {
- setContentVisibility(visible);
- }
-
- @Override
- public void onTaskRemovalStarted(int taskId) {
- // Must post because this is called from a binder thread.
- post(() -> {
- mController.removeTask();
- cleanUpTaskView();
- });
- }
-
- @Override
- public void onBackPressedOnTaskRoot(int taskId) {
- if (mTask.taskId == taskId && !mIsStashed) {
- // TODO: is removing the window the desired behavior?
- post(() -> mController.removeTask());
- }
- }
- };
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
deleted file mode 100644
index d378a177650ad..0000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * 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.wm.shell.floating;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.floating.FloatingTasksController.SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.sysui.ShellSharedConstants;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * Tests for the floating tasks controller.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FloatingTasksControllerTest extends ShellTestCase {
- // Some behavior in the controller constructor is dependent on this so we can only
- // validate if it's working for the real value for those things.
- private static final boolean FLOATING_TASKS_ACTUALLY_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
-
- @Mock private ShellInit mShellInit;
- @Mock private ShellController mShellController;
- @Mock private WindowManager mWindowManager;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Captor private ArgumentCaptor mFloatingTaskLayerCaptor;
-
- private FloatingTasksController mController;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
-
- WindowMetrics windowMetrics = mock(WindowMetrics.class);
- WindowInsets windowInsets = mock(WindowInsets.class);
- Insets insets = Insets.of(0, 0, 0, 0);
- when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
- when(windowMetrics.getWindowInsets()).thenReturn(windowInsets);
- when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
- when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(insets);
-
- // For the purposes of this test, just run everything synchronously
- ShellExecutor shellExecutor = new TestShellExecutor();
- when(mTaskOrganizer.getExecutor()).thenReturn(shellExecutor);
- }
-
- @After
- public void tearDown() {
- if (mController != null) {
- mController.removeTask();
- mController = null;
- }
- }
-
- private void setUpTabletConfig() {
- Configuration config = mock(Configuration.class);
- config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
- mController.setConfig(config);
- }
-
- private void setUpPhoneConfig() {
- Configuration config = mock(Configuration.class);
- config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET - 1;
- mController.setConfig(config);
- }
-
- private void createController() {
- mController = new FloatingTasksController(mContext,
- mShellInit,
- mShellController,
- mock(ShellCommandHandler.class),
- Optional.empty(),
- mWindowManager,
- mTaskOrganizer,
- mock(TaskViewTransitions.class),
- mock(ShellExecutor.class),
- mock(ShellExecutor.class),
- mock(SyncTransactionQueue.class));
- spyOn(mController);
- }
-
- //
- // Shell specific
- //
- @Test
- public void instantiateController_addInitCallback() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
-
- verify(mShellInit, times(1)).addInitCallback(any(), any());
- }
- }
-
- @Test
- public void instantiateController_doesntAddInitCallback() {
- if (!FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
-
- verify(mShellInit, never()).addInitCallback(any(), any());
- }
- }
-
- @Test
- public void onInit_registerConfigChangeListener() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
- mController.onInit();
-
- verify(mShellController, times(1)).addConfigurationChangeListener(any());
- }
- }
-
- @Test
- public void onInit_addExternalInterface() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
- mController.onInit();
-
- verify(mShellController, times(1)).addExternalInterface(
- ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS, any(), any());
- }
- }
-
- //
- // Tests for floating layer, which is only available for tablets.
- //
-
- @Test
- public void testIsFloatingLayerAvailable_true() {
- createController();
- setUpTabletConfig();
- assertThat(mController.isFloatingLayerAvailable()).isTrue();
- }
-
- @Test
- public void testIsFloatingLayerAvailable_false() {
- createController();
- setUpPhoneConfig();
- assertThat(mController.isFloatingLayerAvailable()).isFalse();
- }
-
- //
- // Tests for floating tasks being enabled, guarded by sysprop flag.
- //
-
- @Test
- public void testIsFloatingTasksEnabled_true() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
- assertThat(mController.isFloatingTasksEnabled()).isTrue();
- }
-
- @Test
- public void testIsFloatingTasksEnabled_false() {
- createController();
- mController.setFloatingTasksEnabled(false);
- setUpTabletConfig();
- assertThat(mController.isFloatingTasksEnabled()).isFalse();
- }
-
- //
- // Tests for behavior depending on flags
- //
-
- @Test
- public void testShowTaskIntent_enabled() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
- }
-
- @Test
- public void testShowTaskIntent_notEnabled() {
- createController();
- mController.setFloatingTasksEnabled(false);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager, never()).addView(any(), any());
- }
-
- @Test
- public void testRemoveTask() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
-
- mController.removeTask();
- verify(mWindowManager).removeView(mFloatingTaskLayerCaptor.capture());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(0);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index a21f45f701b34..632fcdc162592 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -97,7 +97,6 @@ public abstract class SystemUIInitializer {
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
.setBackAnimation(mWMComponent.getBackAnimation())
- .setFloatingTasks(mWMComponent.getFloatingTasks())
.setDesktopMode(mWMComponent.getDesktopMode());
// Only initialize when not starting from tests since this currently initializes some
@@ -118,7 +117,6 @@ public abstract class SystemUIInitializer {
.setStartingSurface(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
.setBackAnimation(Optional.ofNullable(null))
- .setFloatingTasks(Optional.ofNullable(null))
.setDesktopMode(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d05bd5120872a..c260b7df2b453 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -40,7 +40,6 @@ import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
-import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
@@ -110,9 +109,6 @@ public interface SysUIComponent {
@BindsInstance
Builder setBackAnimation(Optional b);
- @BindsInstance
- Builder setFloatingTasks(Optional f);
-
@BindsInstance
Builder setDesktopMode(Optional d);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e46bdb7a8ad8d..95919c6b2c0df 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -41,6 +41,7 @@ import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.data.BouncerViewModule;
@@ -242,6 +243,7 @@ public abstract class SystemUIModule {
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
+ FeatureFlags featureFlags,
@Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
bubblesOptional,
@@ -258,6 +260,7 @@ public abstract class SystemUIModule {
notifCollection,
notifPipeline,
sysUiState,
+ featureFlags,
sysuiMainExecutor));
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 096f969493826..d756f3a446557 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -32,7 +32,6 @@ import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
-import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
@@ -111,9 +110,6 @@ public interface WMComponent {
@WMSingleton
Optional getBackAnimation();
- @WMSingleton
- Optional getFloatingTasks();
-
/**
* Optional {@link DesktopMode} component for interacting with desktop mode.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 974f6058a1a36..f51e281966272 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -284,15 +284,6 @@ object Flags {
@Keep
val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)
- @JvmField
- @Keep
- val FLOATING_TASKS_ENABLED = SysPropBooleanFlag(1106, "persist.wm.debug.floating_tasks", false)
-
- @JvmField
- @Keep
- val SHOW_FLOATING_TASKS_AS_BUBBLES =
- SysPropBooleanFlag(1107, "persist.wm.debug.floating_tasks_as_bubbles", false)
-
@JvmField
@Keep
val ENABLE_FLING_TO_DISMISS_BUBBLE =
@@ -308,6 +299,9 @@ object Flags {
val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)
+ // TODO(b/256873975): Tracking Bug
+ @JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111)
+
// 1200 - predictive back
@JvmField
@Keep
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d247f249e2fde..b964b76795b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -22,7 +22,7 @@ import android.os.UserManager
import android.view.KeyEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.kotlin.getOrNull
-import com.android.wm.shell.floating.FloatingTasks
+import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
@@ -39,7 +39,7 @@ internal class NoteTaskController
constructor(
private val context: Context,
private val intentResolver: NoteTaskIntentResolver,
- private val optionalFloatingTasks: Optional,
+ private val optionalBubbles: Optional,
private val optionalKeyguardManager: Optional,
private val optionalUserManager: Optional,
@NoteTaskEnabledKey private val isEnabled: Boolean,
@@ -54,7 +54,7 @@ constructor(
}
private fun showNoteTask() {
- val floatingTasks = optionalFloatingTasks.getOrNull() ?: return
+ val bubbles = optionalBubbles.getOrNull() ?: return
val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
val userManager = optionalUserManager.getOrNull() ?: return
val intent = intentResolver.resolveIntent() ?: return
@@ -66,7 +66,7 @@ constructor(
context.startActivity(intent)
} else {
// TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
- floatingTasks.showOrSetStashed(intent)
+ bubbles.showAppBubble(intent)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index d84717da3a219..0a5b6008981b5 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -17,7 +17,7 @@
package com.android.systemui.notetask
import com.android.systemui.statusbar.CommandQueue
-import com.android.wm.shell.floating.FloatingTasks
+import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -26,7 +26,7 @@ import javax.inject.Inject
internal class NoteTaskInitializer
@Inject
constructor(
- private val optionalFloatingTasks: Optional,
+ private val optionalBubbles: Optional,
private val lazyNoteTaskController: Lazy,
private val commandQueue: CommandQueue,
@NoteTaskEnabledKey private val isEnabled: Boolean,
@@ -40,7 +40,7 @@ constructor(
}
fun initialize() {
- if (isEnabled && optionalFloatingTasks.isPresent) {
+ if (isEnabled && optionalBubbles.isPresent) {
commandQueue.addCallback(callbacks)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 4e77514645645..a4384d5810ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,6 +25,7 @@ import static android.service.notification.NotificationListenerService.REASON_GR
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.systemui.flags.Flags.WM_BUBBLE_BAR;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -51,6 +52,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -129,6 +131,7 @@ public class BubblesManager {
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
+ FeatureFlags featureFlags,
Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context,
@@ -146,6 +149,7 @@ public class BubblesManager {
notifCollection,
notifPipeline,
sysUiState,
+ featureFlags,
sysuiMainExecutor);
} else {
return null;
@@ -168,6 +172,7 @@ public class BubblesManager {
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
+ FeatureFlags featureFlags,
Executor sysuiMainExecutor) {
mContext = context;
mBubbles = bubbles;
@@ -352,6 +357,7 @@ public class BubblesManager {
});
}
};
+ mBubbles.setBubbleBarEnabled(featureFlags.isEnabled(WM_BUBBLE_BAR));
mBubbles.setSysuiProxy(mSysuiProxy);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index f20c6a29b8406..9758842d1e353 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -25,8 +25,8 @@ import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
import com.android.systemui.util.mockito.whenever
-import com.android.wm.shell.floating.FloatingTasks
-import java.util.*
+import com.android.wm.shell.bubbles.Bubbles
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,8 +49,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
@Mock lateinit var context: Context
@Mock lateinit var noteTaskIntentResolver: NoteTaskIntentResolver
- @Mock lateinit var floatingTasks: FloatingTasks
- @Mock lateinit var optionalFloatingTasks: Optional
+ @Mock lateinit var bubbles: Bubbles
+ @Mock lateinit var optionalBubbles: Optional
@Mock lateinit var keyguardManager: KeyguardManager
@Mock lateinit var optionalKeyguardManager: Optional
@Mock lateinit var optionalUserManager: Optional
@@ -61,7 +61,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(notesIntent)
- whenever(optionalFloatingTasks.orElse(null)).thenReturn(floatingTasks)
+ whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
whenever(optionalUserManager.orElse(null)).thenReturn(userManager)
whenever(userManager.isUserUnlocked).thenReturn(true)
@@ -71,7 +71,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
return NoteTaskController(
context = context,
intentResolver = noteTaskIntentResolver,
- optionalFloatingTasks = optionalFloatingTasks,
+ optionalBubbles = optionalBubbles,
optionalKeyguardManager = optionalKeyguardManager,
optionalUserManager = optionalUserManager,
isEnabled = isEnabled,
@@ -85,16 +85,16 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_keyguardIsUnlocked_shouldStartFloatingTask() {
+ fun handleSystemKey_keyguardIsUnlocked_shouldStartBubbles() {
whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
- verify(floatingTasks).showOrSetStashed(notesIntent)
+ verify(bubbles).showAppBubble(notesIntent)
verify(context, never()).startActivity(notesIntent)
}
@@ -103,17 +103,17 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_floatingTasksIsNull_shouldDoNothing() {
- whenever(optionalFloatingTasks.orElse(null)).thenReturn(null)
+ fun handleSystemKey_bubblesIsNull_shouldDoNothing() {
+ whenever(optionalBubbles.orElse(null)).thenReturn(null)
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -123,7 +123,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -133,7 +133,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -143,7 +143,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -151,7 +151,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController(isEnabled = false).handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
@@ -161,6 +161,6 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
verify(context, never()).startActivity(notesIntent)
- verify(floatingTasks, never()).showOrSetStashed(notesIntent)
+ verify(bubbles, never()).showAppBubble(notesIntent)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index f344c8d9eec47..334089c43e270 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -21,8 +21,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.wm.shell.floating.FloatingTasks
-import java.util.*
+import com.android.wm.shell.bubbles.Bubbles
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,20 +43,20 @@ import org.mockito.MockitoAnnotations
internal class NoteTaskInitializerTest : SysuiTestCase() {
@Mock lateinit var commandQueue: CommandQueue
- @Mock lateinit var floatingTasks: FloatingTasks
- @Mock lateinit var optionalFloatingTasks: Optional
+ @Mock lateinit var bubbles: Bubbles
+ @Mock lateinit var optionalBubbles: Optional
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(optionalFloatingTasks.isPresent).thenReturn(true)
- whenever(optionalFloatingTasks.orElse(null)).thenReturn(floatingTasks)
+ whenever(optionalBubbles.isPresent).thenReturn(true)
+ whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
}
private fun createNoteTaskInitializer(isEnabled: Boolean = true): NoteTaskInitializer {
return NoteTaskInitializer(
- optionalFloatingTasks = optionalFloatingTasks,
+ optionalBubbles = optionalBubbles,
lazyNoteTaskController = mock(),
commandQueue = commandQueue,
isEnabled = isEnabled,
@@ -78,8 +78,8 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
}
@Test
- fun initialize_floatingTasksNotPresent_shouldDoNothing() {
- whenever(optionalFloatingTasks.isPresent).thenReturn(false)
+ fun initialize_bubblesNotPresent_shouldDoNothing() {
+ whenever(optionalBubbles.isPresent).thenReturn(false)
createNoteTaskInitializer().initialize()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fa7ebf6a2449f..bee882d438496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -86,6 +86,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -394,6 +395,7 @@ public class BubblesTest extends SysuiTestCase {
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
+ mock(FeatureFlags.class),
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);