Show a persistent toast when selecting desktop apps on home screen
When desktop stashed state changes, show a persistent popup on home
screen to select an app that will be launched on desktop.
Flag: persist.wm.debug.desktop_mode_2
Bug: 261234402
Test: launch an app on desktop, swipe home, observe the popup, launch an
app, observe app is launched on desktop and popup is hidden
Test: stash desktop apps by going home while on desktop, press on the
"exit desktop" button in the popup, observe popup is hidden, launch an
app, observe it is launched in fullscreen
Change-Id: I66fe0ab977fa7b2059f149d7d0ab0cf92192c967
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 0000000..d7df338
--- /dev/null
+++ b/quickstep/res/drawable/bg_floating_desktop_select.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+
+ <corners android:radius="@dimen/rounded_button_radius" />
+ <solid android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</shape>
\ 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 0000000..375fc44
--- /dev/null
+++ b/quickstep/res/layout/floating_desktop_app_select.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+-->
+
+<com.android.quickstep.views.DesktopAppSelectView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/desktop_mode_floating_app_select_height"
+ android:layout_gravity="top|center_horizontal"
+ android:background="@drawable/bg_floating_desktop_select"
+ android:elevation="@dimen/desktop_mode_floating_app_select_elevation"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/desktop_app_select_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_text_margin"
+ android:layout_marginStart="@dimen/desktop_mode_floating_app_select_margin"
+ android:drawablePadding="@dimen/desktop_mode_floating_app_select_text_margin"
+ android:drawableStart="@drawable/ic_desktop"
+ android:drawableTint="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:fontFamily="google-sans-medium"
+ android:gravity="center_vertical"
+ android:text="@string/desktop_select_app_toast"
+ android:textColor="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
+
+ <Button
+ android:id="@+id/close_button"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_margin"
+ android:minWidth="0dp"
+ android:fontFamily="google-sans-medium"
+ android:text="@string/desktop_button_close_app_toast"
+ android:textAllCaps="false"
+ android:textColor="?androidprv:attr/materialColorPrimary"
+ android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
+
+</com.android.quickstep.views.DesktopAppSelectView>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index bb4f74d..c9f84c7 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -383,4 +383,12 @@
<dimen name="keyboard_quick_switch_task_view_radius">16dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
+
+ <!-- Desktop mode -->
+ <dimen name="desktop_mode_floating_app_select_height">56dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_elevation">4dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_margin">16dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_text_size">14sp</dimen>
+ <dimen name="desktop_mode_floating_app_select_text_margin">8dp</dimen>
+
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 2d8c45a..ebcc817 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -298,4 +298,10 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+
+ <!-- ******* Desktop ******* -->
+ <!-- Text shown in popup to choose a desktop app. [CHAR LIMIT=60] -->
+ <string name="desktop_select_app_toast">Adding app to Desktop</string>
+ <!-- Text shown on a button that closes the popup for choosing a desktop app. [CHAR_LIMIT=40] -->
+ <string name="desktop_button_close_app_toast">Cancel</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 479dc82..b052deb 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -17,11 +17,9 @@
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.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 @@
@Nullable
private IDesktopTaskListener mDesktopTaskListener;
+ private DesktopAppSelectView mSelectAppToast;
public DesktopVisibilityController(Launcher launcher) {
mLauncher = launcher;
@@ -60,17 +60,22 @@
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 @@
if (!isDesktopModeSupported()) {
return;
}
+
if (freeformTasksVisible != mFreeformTasksVisible) {
mFreeformTasksVisible = freeformTasksVisible;
if (mFreeformTasksVisible) {
@@ -219,4 +225,28 @@
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 0be9741..0577aee 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -1161,6 +1161,17 @@
}
}
+ /** 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 0000000..53101fb
--- /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;
+ }
+}