diff --git a/quickstep/res/drawable/bg_floating_desktop_select.xml b/quickstep/res/drawable/bg_floating_desktop_select.xml
new file mode 100644
index 0000000000..d7df338e93
--- /dev/null
+++ b/quickstep/res/drawable/bg_floating_desktop_select.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml
new file mode 100644
index 0000000000..375fc44b73
--- /dev/null
+++ b/quickstep/res/layout/floating_desktop_app_select.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index bb4f74d2eb..c9f84c7159 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -383,4 +383,12 @@
16dp
24dp
8dp
+
+
+ 56dp
+ 4dp
+ 16dp
+ 14sp
+ 8dp
+
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 2d8c45a4dc..ebcc8172bd 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -298,4 +298,10 @@
%1$s and %2$s
+
+
+
+ Adding app to Desktop
+
+ Cancel
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 479dc82e40..b052deb6ba 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -17,11 +17,9 @@ package com.android.launcher3.statehandlers;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
-import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -30,6 +28,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.DesktopAppSelectView;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
/**
@@ -49,6 +48,7 @@ public class DesktopVisibilityController {
@Nullable
private IDesktopTaskListener mDesktopTaskListener;
+ private DesktopAppSelectView mSelectAppToast;
public DesktopVisibilityController(Launcher launcher) {
mLauncher = launcher;
@@ -60,17 +60,22 @@ public class DesktopVisibilityController {
public void registerSystemUiListener() {
mDesktopTaskListener = new IDesktopTaskListener.Stub() {
@Override
- public void onVisibilityChanged(int displayId, boolean visible) throws RemoteException {
+ public void onVisibilityChanged(int displayId, boolean visible) {
// TODO(b/261234402): move visibility from sysui state to listener
}
@Override
- public void onStashedChanged(int displayId, boolean stashed) throws RemoteException {
- // TODO(b/261234402): show a persistent toast
+ public void onStashedChanged(int displayId, boolean stashed) {
MAIN_EXECUTOR.execute(() -> {
- if (stashed && displayId == mLauncher.getDisplayId()) {
- Toast.makeText(mLauncher, "Adding app to Desktop",
- Toast.LENGTH_SHORT).show();
+ if (displayId == mLauncher.getDisplayId()) {
+ if (DEBUG) {
+ Log.d(TAG, "desktop stashed changed value=" + stashed);
+ }
+ if (stashed) {
+ showSelectAppToast();
+ } else {
+ hideSelectAppToast();
+ }
}
});
}
@@ -110,6 +115,7 @@ public class DesktopVisibilityController {
if (!isDesktopModeSupported()) {
return;
}
+
if (freeformTasksVisible != mFreeformTasksVisible) {
mFreeformTasksVisible = freeformTasksVisible;
if (mFreeformTasksVisible) {
@@ -219,4 +225,28 @@ public class DesktopVisibilityController {
activity.setResumed();
}
}
+
+ private void showSelectAppToast() {
+ if (mSelectAppToast != null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "show toast to select desktop apps");
+ }
+ Runnable onCloseCallback = () -> {
+ SystemUiProxy.INSTANCE.get(mLauncher).hideStashedDesktopApps(mLauncher.getDisplayId());
+ };
+ mSelectAppToast = DesktopAppSelectView.show(mLauncher, onCloseCallback);
+ }
+
+ private void hideSelectAppToast() {
+ if (mSelectAppToast == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "hide toast to select desktop apps");
+ }
+ mSelectAppToast.hide();
+ mSelectAppToast = null;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0be97413cd..0577aeec93 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -1161,6 +1161,17 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
+ /** Call shell to hide desktop apps that may be stashed */
+ public void hideStashedDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.hideStashedDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call hideStashedDesktopApps", e);
+ }
+ }
+ }
+
/** Call shell to get number of visible freeform tasks */
public int getVisibleDesktopTaskCount(int displayId) {
if (mDesktopMode != null) {
diff --git a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
new file mode 100644
index 0000000000..53101fbc49
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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.quickstep.views;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+
+/**
+ * Floating view show on launcher home screen that notifies the user that an app will be launched to
+ * the desktop.
+ */
+public class DesktopAppSelectView extends LinearLayout {
+
+ private static final int HIDE_DURATION = 83;
+
+ private final Launcher mLauncher;
+
+ @Nullable
+ private Runnable mOnCloseCallback = null;
+ private boolean mIsHideAnimationRunning;
+
+ public DesktopAppSelectView(Context context) {
+ this(context, null);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mLauncher = Launcher.getLauncher(context);
+ }
+
+ /**
+ * Show the popup on launcher home screen
+ *
+ * @param onCloseCallback optional callback that is called when user clicks the close button
+ * @return the created view
+ */
+ public static DesktopAppSelectView show(Launcher launcher, @Nullable Runnable onCloseCallback) {
+ DesktopAppSelectView view = (DesktopAppSelectView) launcher.getLayoutInflater().inflate(
+ R.layout.floating_desktop_app_select, launcher.getDragLayer(), false);
+ view.setOnCloseClickCallback(onCloseCallback);
+ launcher.getDragLayer().addView(view);
+ return view;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ findViewById(R.id.close_button).setOnClickListener(v -> {
+ if (!mIsHideAnimationRunning) {
+ hide();
+ if (mOnCloseCallback != null) {
+ mOnCloseCallback.run();
+ }
+ }
+ });
+ }
+
+ /**
+ * Hide the floating view
+ */
+ public void hide() {
+ if (!mIsHideAnimationRunning) {
+ mIsHideAnimationRunning = true;
+ animate().alpha(0).setDuration(HIDE_DURATION).setInterpolator(LINEAR).withEndAction(
+ () -> {
+ mLauncher.getDragLayer().removeView(this);
+ mIsHideAnimationRunning = false;
+ });
+ }
+ }
+
+ /**
+ * Add a callback that is called when close button is clicked
+ */
+ public void setOnCloseClickCallback(@Nullable Runnable callback) {
+ mOnCloseCallback = callback;
+ }
+}