Merge "Update DeviceProfileDumpTest to work with flag on and off." into main
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index edbea88..80d2eac 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -179,7 +179,7 @@
</intent-filter>
</activity>
- <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
+ <!-- Disable eager initialization of Jetpack libraries. See bug 197780098. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index dd78ca4..8682e5d 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -277,3 +277,17 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enabled_folders_in_all_apps"
+ namespace: "launcher"
+ description: "Enables folders in all apps"
+ bug: "341582436"
+}
+
+flag {
+ name: "enable_tiny_taskbar"
+ namespace: "launcher"
+ description: "Enables Taskbar on phones"
+ bug: "341784466"
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 0eb8775..26ca06a 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -56,7 +56,7 @@
import com.android.quickstep.util.AssistContentRequester;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.GoOverviewActionsView;
-import com.android.quickstep.views.TaskThumbnailViewDeprecated;
+import com.android.quickstep.views.TaskView.TaskContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -101,8 +101,8 @@
/**
* Create a new overlay instance for the given View
*/
- public TaskOverlayGo createOverlay(TaskThumbnailViewDeprecated thumbnailView) {
- return new TaskOverlayGo(thumbnailView, mContentRequester);
+ public TaskOverlayGo createOverlay(TaskContainer taskContainer) {
+ return new TaskOverlayGo(taskContainer, mContentRequester);
}
/**
@@ -120,9 +120,9 @@
private OverlayDialogGo mDialog;
private ArrowTipView mArrowTipView;
- private TaskOverlayGo(TaskThumbnailViewDeprecated taskThumbnailView,
+ private TaskOverlayGo(TaskContainer taskContainer,
AssistContentRequester assistContentRequester) {
- super(taskThumbnailView);
+ super(taskContainer);
mFactoryContentRequester = assistContentRequester;
mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
}
@@ -148,7 +148,8 @@
// Disable Overview Actions for Work Profile apps
boolean isManagedProfileTask =
UserManager.get(mApplicationContext).isManagedProfile(task.key.userId);
- boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot() && !isManagedProfileTask;
+ boolean isAllowedByPolicy = mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot()
+ && !isManagedProfileTask;
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
mTaskPackageName = task.key.getPackageName();
mSharedPreferences = LauncherPrefs.getPrefs(mApplicationContext);
@@ -162,8 +163,7 @@
int taskId = task.key.id;
mFactoryContentRequester.requestAssistContent(taskId, this::onAssistContentReceived);
- RecentsOrientedState orientedState =
- mThumbnailView.getTaskView().getRecentsView().getPagedViewOrientedState();
+ RecentsOrientedState orientedState = mTaskContainer.getTaskView().getOrientedState();
boolean isInLandscape = orientedState.getDisplayRotation() != ROTATION_0;
// show tooltips in portrait mode only
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 7c648b6..823c821 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -121,6 +121,20 @@
}
message TaskSwitcherContainer {
+ /**
+ * Indicates the current OrientationHandler in use in Overview.
+ * In fake landscape, the value will be
+ * {@link com.android.quickstep.orientation.LandscapePagedViewHandler} and in real landscape,
+ * the value will be {@link com.android.quickstep.orientation.PortraitPagedViewHandler} for
+ * example.
+ */
+ optional OrientationHandler orientation_handler = 1;
+
+ enum OrientationHandler {
+ PORTRAIT = 0;
+ LANDSCAPE = 1;
+ SEASCAPE = 2;
+ }
}
// Container for taskbar.
diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml
deleted file mode 100644
index 375fc44..0000000
--- a/quickstep/res/layout/floating_desktop_app_select.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?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/layout/taskbar_nav_button.xml b/quickstep/res/layout/taskbar_nav_button.xml
index aea4885..8f1c904 100644
--- a/quickstep/res/layout/taskbar_nav_button.xml
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -19,6 +19,7 @@
android:layout_width="@dimen/taskbar_nav_buttons_size"
android:layout_height="@dimen/taskbar_nav_buttons_size"
android:background="@drawable/taskbar_icon_click_feedback_roundrect"
+ android:focusable="false"
android:scaleType="center"
android:tint="@color/taskbar_nav_icon_light_color"
tools:ignore="UseAppTint" />
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 7f09b6e..2021a0b 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -484,11 +484,4 @@
<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 278c66a..cf987c3 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -332,10 +332,4 @@
<!-- 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/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 72aaa90..0c499b8 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -375,7 +375,7 @@
*/
protected boolean isLaunchingFromRecents(@NonNull View v,
@Nullable RemoteAnimationTarget[] targets) {
- return mLauncher.getStateManager().getState().overviewUi
+ return mLauncher.getStateManager().getState().isRecentsViewVisible
&& findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 9eabb55..62cc0bb 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -35,7 +35,6 @@
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.GestureState;
import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.views.DesktopAppSelectView;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import java.util.HashSet;
@@ -49,8 +48,6 @@
private static final String TAG = "DesktopVisController";
private static final boolean DEBUG = false;
- private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_stashing", false);
private final Launcher mLauncher;
private final Set<DesktopVisibilityListener> mDesktopVisibilityListeners = new HashSet<>();
@@ -61,7 +58,6 @@
@Nullable
private IDesktopTaskListener mDesktopTaskListener;
- private DesktopAppSelectView mSelectAppToast;
public DesktopVisibilityController(Launcher launcher) {
mLauncher = launcher;
@@ -86,21 +82,7 @@
@Override
public void onStashedChanged(int displayId, boolean stashed) {
- if (!IS_STASHING_ENABLED) {
- return;
- }
- MAIN_EXECUTOR.execute(() -> {
- if (displayId == mLauncher.getDisplayId()) {
- if (DEBUG) {
- Log.d(TAG, "desktop stashed changed value=" + stashed);
- }
- if (stashed) {
- showSelectAppToast();
- } else {
- hideSelectAppToast();
- }
- }
- });
+ Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated");
}
};
SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener);
@@ -111,6 +93,7 @@
*/
public void unregisterSystemUiListener() {
SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null);
+ mDesktopTaskListener = null;
}
/**
@@ -190,7 +173,7 @@
}
setBackgroundStateEnabled(state == BACKGROUND_APP);
// Desktop visibility tracks overview and background state separately
- setOverviewStateEnabled(state != BACKGROUND_APP && state.overviewUi);
+ setOverviewStateEnabled(state != BACKGROUND_APP && state.isRecentsViewVisible);
}
private void setOverviewStateEnabled(boolean overviewStateEnabled) {
@@ -303,15 +286,6 @@
}
/**
- * Handle launcher moving to home due to home gesture or home button press.
- */
- public void onHomeActionTriggered() {
- if (IS_STASHING_ENABLED && areDesktopTasksVisible()) {
- SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId());
- }
- }
-
- /**
* TODO: b/333533253 - Remove after flag rollout
*/
private void setLauncherViewsVisibility(int visibility) {
@@ -373,30 +347,6 @@
}
}
- 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;
- }
-
/** A listener for when the user enters/exits Desktop Mode. */
public interface DesktopVisibilityListener {
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 48fc7d1..39b4f77 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -186,7 +186,7 @@
@NonNull ImageView thumbnailView,
@ColorInt int backgroundColor,
@Nullable ThumbnailData thumbnailData) {
- Bitmap bm = thumbnailData == null ? null : thumbnailData.thumbnail;
+ Bitmap bm = thumbnailData == null ? null : thumbnailData.getThumbnail();
if (thumbnailView.getVisibility() != VISIBLE) {
thumbnailView.setVisibility(VISIBLE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 89dfff3..81581b8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -45,6 +45,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import android.animation.ArgbEvaluator;
@@ -134,6 +135,7 @@
private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
private static final int FLAG_SMALL_SCREEN = 1 << 13;
private static final int FLAG_SLIDE_IN_VIEW_VISIBLE = 1 << 14;
+ private static final int FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING = 1 << 15;
/**
* Flags where a UI could be over Taskbar surfaces, so the color override should be disabled.
@@ -286,8 +288,9 @@
// - Notification shade is expanded
// - IME is showing (add separate translation for IME)
// - VoiceInteractionWindow (assistant) is showing
+ // - Keyboard shortcuts helper is showing
int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
- | FLAG_VOICE_INTERACTION_WINDOW_SHOWING;
+ | FLAG_VOICE_INTERACTION_WINDOW_SHOWING | FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING;
mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
1, 0));
@@ -458,6 +461,8 @@
boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
boolean isVoiceInteractionWindowShowing =
(sysUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0;
+ boolean isKeyboardShortcutHelperShowing =
+ (sysUiStateFlags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0;
// TODO(b/202218289) we're getting IME as not visible on lockscreen from system
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
@@ -469,6 +474,7 @@
updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
updateStateForFlag(FLAG_VOICE_INTERACTION_WINDOW_SHOWING, isVoiceInteractionWindowShowing);
+ updateStateForFlag(FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING, isKeyboardShortcutHelperShowing);
if (mA11yButton != null) {
// Only used in 3 button
@@ -1056,6 +1062,8 @@
appendFlag(str, flags, FLAG_SCREEN_PINNING_ACTIVE, "FLAG_SCREEN_PINNING_ACTIVE");
appendFlag(str, flags, FLAG_VOICE_INTERACTION_WINDOW_SHOWING,
"FLAG_VOICE_INTERACTION_WINDOW_SHOWING");
+ appendFlag(str, flags, FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING,
+ "FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING");
return str.toString();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e77922a..1571ac0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -37,6 +37,7 @@
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
@@ -289,7 +290,7 @@
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
mWindowManager,
new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
- getMainThreadHandler())),
+ UI_HELPER_EXECUTOR.getHandler(), getMainThreadHandler())),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 7f201b4..84874a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -37,7 +37,7 @@
import androidx.core.graphics.Insets;
import androidx.core.view.WindowInsetsCompat;
-import com.android.app.viewcapture.SettingsAwareViewCapture;
+import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -150,7 +150,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
- mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext())
+ mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
.startCapture(getRootView(), ".Taskbar");
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index d516d87..ead1a8a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -669,7 +669,7 @@
}
boolean isInOverviewUi() {
- return mLauncherState.overviewUi;
+ return mLauncherState.isRecentsViewVisible;
}
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 645f3ee..d26a36d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -44,10 +44,8 @@
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.AssistUtils;
@@ -285,13 +283,6 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
-
- DesktopVisibilityController desktopVisibilityController =
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- desktopVisibilityController.onHomeActionTriggered();
- }
-
mCallbacks.onNavigateHome();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 1a3c497..74d2d60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -424,14 +424,15 @@
* @see android.view.WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if ((mActivity.isPhoneMode() && !mActivity.isThreeButtonNav())
- || DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode()
+ || DisplayController.isTransientTaskbar(mActivity))) {
return getStashedHeight();
}
if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
DeviceProfile dp = mActivity.getDeviceProfile();
- if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent) {
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && (dp.isTaskbarPresent
+ || mActivity.isPhoneGestureNavMode())) {
// We always show the back button in SUW but in portrait the SUW layout may not
// be wide enough to support overlapping the nav bar with its content.
// We're sending different res values in portrait vs landscape
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index d0c494c..2b68b52 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -269,8 +269,8 @@
foundTaskView,
foundTask,
taskContainer.getIconView().getDrawable(),
- taskContainer.getThumbnailView(),
- taskContainer.getThumbnailView().getThumbnail(),
+ taskContainer.getThumbnailViewDeprecated(),
+ taskContainer.getThumbnailViewDeprecated().getThumbnail(),
null /* intent */,
null /* user */,
info);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 400c3ab..046f5b6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -31,8 +31,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
-import static java.lang.Math.abs;
-
import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.app.Notification;
@@ -47,7 +45,6 @@
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Point;
-import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -67,8 +64,6 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -148,13 +143,14 @@
private BubbleBarItem mSelectedBubble;
private BubbleBarOverflow mOverflowBubble;
+ private ImeVisibilityChecker mImeVisibilityChecker;
private BubbleBarViewController mBubbleBarViewController;
private BubbleStashController mBubbleStashController;
private BubbleStashedHandleViewController mBubbleStashedHandleViewController;
private BubblePinController mBubblePinController;
- // Keep track of bubble bar bounds sent to shell to avoid sending duplicate updates
- private final Rect mLastSentBubbleBarBounds = new Rect();
+ // Cache last sent top coordinate to avoid sending duplicate updates to shell
+ private int mLastSentBubbleBarTop;
/**
* Similar to {@link BubbleBarUpdate} but rather than {@link BubbleInfo}s it uses
@@ -216,7 +212,10 @@
mSystemUiProxy.setBubblesListener(null);
}
- public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
+ /** Initializes controllers. */
+ public void init(BubbleControllers bubbleControllers,
+ ImeVisibilityChecker imeVisibilityChecker) {
+ mImeVisibilityChecker = imeVisibilityChecker;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
@@ -321,9 +320,9 @@
// enabling gesture nav. also suppress animation if the bubble bar is hidden for sysui e.g.
// the shade is open, or we're locked.
final boolean suppressAnimation =
- update.initialState || mBubbleBarViewController.isHiddenForSysui();
+ update.initialState || mBubbleBarViewController.isHiddenForSysui()
+ || mImeVisibilityChecker.isImeVisible();
- BubbleBarItem previouslySelectedBubble = mSelectedBubble;
BubbleBarBubble bubbleToSelect = null;
if (!update.removedBubbles.isEmpty()) {
for (int i = 0; i < update.removedBubbles.size(); i++) {
@@ -379,6 +378,7 @@
BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
// If we're not stashed, we're visible so animate
bb.getView().updateDotVisibility(!mBubbleStashController.isStashed() /* animate */);
+ mBubbleBarViewController.animateBubbleNotification(bb, /* isExpanding= */ false);
}
if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
// Create the new list
@@ -441,9 +441,8 @@
info.getFlags() | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
mSelectedBubble.getView().updateDotVisibility(true /* animate */);
}
- Rect bounds = getExpandedBubbleBarDisplayBounds();
- mLastSentBubbleBarBounds.set(bounds);
- mSystemUiProxy.showBubble(getSelectedBubbleKey(), bounds);
+ mLastSentBubbleBarTop = mBarView.getRestingTopPositionOnScreen();
+ mSystemUiProxy.showBubble(getSelectedBubbleKey(), mLastSentBubbleBarTop);
} else {
Log.w(TAG, "Trying to show the selected bubble but it's null");
}
@@ -632,39 +631,17 @@
return mIconFactory.createBadgedIconBitmap(drawable).icon;
}
- private void onBubbleBarBoundsChanged(Rect newBounds) {
- Rect displayBounds = convertToDisplayBounds(newBounds);
- // Only send bounds over if they changed
- if (!displayBounds.equals(mLastSentBubbleBarBounds)) {
- mLastSentBubbleBarBounds.set(displayBounds);
- mSystemUiProxy.setBubbleBarBounds(displayBounds);
+ private void onBubbleBarBoundsChanged() {
+ int newTop = mBarView.getRestingTopPositionOnScreen();
+ if (newTop != mLastSentBubbleBarTop) {
+ mLastSentBubbleBarTop = newTop;
+ mSystemUiProxy.updateBubbleBarTopOnScreen(newTop);
}
}
- /**
- * Get bounds of the bubble bar as if it would be expanded.
- * Calculates the bounds instead of retrieving current view location as the view may be
- * animating.
- */
- private Rect getExpandedBubbleBarDisplayBounds() {
- return convertToDisplayBounds(mBarView.getBubbleBarBounds());
- }
-
- private Rect convertToDisplayBounds(Rect currentBarBounds) {
- Point displaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
- Rect displayBounds = new Rect();
- // currentBarBounds is only useful for distance from left or right edge.
- // It contains the current bounds, calculate the expanded bounds.
- if (mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl())) {
- displayBounds.left = currentBarBounds.left;
- displayBounds.right = (int) (currentBarBounds.left + mBarView.expandedWidth());
- } else {
- displayBounds.left = (int) (currentBarBounds.right - mBarView.expandedWidth());
- displayBounds.right = currentBarBounds.right;
- }
- final int translation = (int) abs(mBubbleStashController.getBubbleBarTranslationY());
- displayBounds.top = displaySize.y - currentBarBounds.height() - translation;
- displayBounds.bottom = displaySize.y - translation;
- return displayBounds;
+ /** Interface for checking whether the IME is visible. */
+ public interface ImeVisibilityChecker {
+ /** Whether the IME is visible. */
+ boolean isImeVisible();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index f3ac1e4..23e52e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.util.DisplayController;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import java.util.List;
@@ -394,21 +395,24 @@
* from the internal location that was used during bubble bar layout, translation values are
* calculated to position the bar at the desired location.
*
- * @param initialTranslation initial bubble bar translation at the start of drag
+ * @param initialTranslation initial bubble translation inside the bar at the start of drag
* @param location desired location of the bubble bar when drag is released
* @return point with x and y values representing translation on x and y-axis
*/
public PointF getDraggedBubbleReleaseTranslation(PointF initialTranslation,
BubbleBarLocation location) {
- // Start with bubble bar translation
- final PointF dragEndTranslation = new PointF(
- getBubbleBarDragReleaseTranslation(initialTranslation, location));
- // Apply individual bubble translation, as the order may have changed
- int viewIndex = indexOfChild(mDraggedBubbleView);
- dragEndTranslation.x += getExpandedBubbleTranslationX(viewIndex,
- getChildCount(),
- location.isOnLeft(isLayoutRtl()));
- return dragEndTranslation;
+ float dragEndTranslationX = initialTranslation.x;
+ boolean newLocationOnLeft = location.isOnLeft(isLayoutRtl());
+ if (getBubbleBarLocation().isOnLeft(isLayoutRtl()) != newLocationOnLeft) {
+ // Calculate translationX based on bar and bubble translations
+ float bubbleBarTx = getBubbleBarDragReleaseTranslation(initialTranslation, location).x;
+ float bubbleTx =
+ getExpandedBubbleTranslationX(
+ indexOfChild(mDraggedBubbleView), getChildCount(), newLocationOnLeft);
+ dragEndTranslationX = bubbleBarTx + bubbleTx;
+ }
+ // translationY does not change during drag and can be reused
+ return new PointF(dragEndTranslationX, initialTranslation.y);
}
private float getDistanceFromOtherSide() {
@@ -551,6 +555,15 @@
}
/**
+ * Get bubble bar top coordinate on screen when bar is resting
+ */
+ public int getRestingTopPositionOnScreen() {
+ int displayHeight = DisplayController.INSTANCE.get(getContext()).getInfo().currentSize.y;
+ int bubbleBarHeight = getBubbleBarBounds().height();
+ return displayHeight - bubbleBarHeight + (int) mController.getBubbleBarTranslationY();
+ }
+
+ /**
* Updates the bounds with translation that may have been applied and returns the result.
*/
public Rect getBubbleBarBounds() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index fbdb2ed..f614dc6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -93,8 +93,6 @@
@Nullable
private BubbleBarBoundsChangeListener mBoundsChangeListener;
- private final Rect mPreviousBubbleBarBounds = new Rect();
-
public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
mActivity = activity;
mBarView = barView;
@@ -122,12 +120,8 @@
mBarView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
- Rect bubbleBarBounds = mBarView.getBubbleBarBounds();
- if (!bubbleBarBounds.equals(mPreviousBubbleBarBounds)) {
- mPreviousBubbleBarBounds.set(bubbleBarBounds);
- if (mBoundsChangeListener != null) {
- mBoundsChangeListener.onBoundsChanged(bubbleBarBounds);
- }
+ if (mBoundsChangeListener != null) {
+ mBoundsChangeListener.onBoundsChanged();
}
});
@@ -414,31 +408,31 @@
b.getView().setOnClickListener(mBubbleClickListener);
mBubbleDragController.setupBubbleView(b.getView());
- if (suppressAnimation) {
+ if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) {
return;
}
-
- if (!(b instanceof BubbleBarBubble bubble)) {
- return;
- }
-
- boolean isInApp = mTaskbarStashController.isInApp();
- // if this is the first bubble, animate to the initial state. one bubble is the overflow
- // so check for at most 2 children.
- if (mBarView.getChildCount() <= 2) {
- mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding);
- return;
- }
-
- // only animate the new bubble if we're in an app and not auto expanding
- if (isInApp && !isExpanding && !isExpanded()) {
- mBubbleBarViewAnimator.animateBubbleInForStashed(bubble);
- }
+ animateBubbleNotification(bubble, isExpanding);
} else {
Log.w(TAG, "addBubble, bubble was null!");
}
}
+ /** Animates the bubble bar to notify the user about a bubble change. */
+ public void animateBubbleNotification(BubbleBarBubble bubble, boolean isExpanding) {
+ boolean isInApp = mTaskbarStashController.isInApp();
+ // if this is the first bubble, animate to the initial state. one bubble is the overflow
+ // so check for at most 2 children.
+ if (mBarView.getChildCount() <= 2) {
+ mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding);
+ return;
+ }
+
+ // only animate the new bubble if we're in an app and not auto expanding
+ if (isInApp && !isExpanding && !isExpanded()) {
+ mBubbleBarViewAnimator.animateBubbleInForStashed(bubble);
+ }
+ }
+
/**
* Reorders the bubbles based on the provided list.
*/
@@ -497,7 +491,7 @@
* that a bubble is being dragged to dismiss.
* @param bubbleView dragged bubble view
*/
- public void onDragStart(@NonNull BubbleView bubbleView) {
+ public void onBubbleDragStart(@NonNull BubbleView bubbleView) {
if (bubbleView.getBubble() == null) return;
mSystemUiProxy.startBubbleDrag(bubbleView.getBubble().getKey());
@@ -506,23 +500,20 @@
/**
* Notifies SystemUI to expand the selected bubble when the bubble is released.
- * @param bubbleView dragged bubble view
*/
- public void onDragRelease(@NonNull BubbleView bubbleView, BubbleBarLocation location) {
- if (bubbleView.getBubble() == null) return;
- // TODO(b/330585402): send new bubble bar bounds to shell for the animation
- mSystemUiProxy.stopBubbleDrag(bubbleView.getBubble().getKey(), location);
+ public void onBubbleDragRelease(BubbleBarLocation location) {
+ mSystemUiProxy.stopBubbleDrag(location, mBarView.getRestingTopPositionOnScreen());
}
/**
* Notifies {@link BubbleBarView} that drag and all animations are finished.
*/
- public void onDragBubbleEnded() {
+ public void onBubbleDragEnd() {
mBarView.setDraggedBubble(null);
}
/** Notifies that dragging the bubble bar ended. */
- public void onDragBubbleBarEnded() {
+ public void onBubbleBarDragEnd() {
// we may have changed the bubble bar translation Y value from the value it had at the
// beginning of the drag, so update the translation Y animator state
mBubbleBarTranslationY.updateValue(mBarView.getTranslationY());
@@ -556,7 +547,7 @@
* @param bubble dismissed bubble item
*/
public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) {
- mSystemUiProxy.removeBubble(bubble.getKey());
+ mSystemUiProxy.dragBubbleToDismiss(bubble.getKey());
}
/**
@@ -578,6 +569,6 @@
*/
public interface BubbleBarBoundsChangeListener {
/** Called when bounds have changed */
- void onBoundsChanged(Rect newBounds);
+ void onBoundsChanged();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index 295477c..32d6375 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -64,7 +64,8 @@
* in constructors for now, as some controllers may still be waiting for init().
*/
public void init(TaskbarControllers taskbarControllers) {
- bubbleBarController.init(taskbarControllers, this);
+ bubbleBarController.init(this,
+ taskbarControllers.navbarButtonsViewController::isImeVisible);
bubbleBarViewController.init(taskbarControllers, this);
bubbleStashedHandleViewController.init(taskbarControllers, this);
bubbleStashController.init(taskbarControllers, this);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index f4b393a..2ebc3e8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -97,7 +97,7 @@
@Override
void onDragStart() {
mBubblePinController.setListener(mLocationChangeListener);
- mBubbleBarViewController.onDragStart(bubbleView);
+ mBubbleBarViewController.onBubbleDragStart(bubbleView);
mBubblePinController.onDragStart(
mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
bubbleView.isLayoutRtl()));
@@ -113,7 +113,7 @@
@Override
protected void onDragRelease() {
mBubblePinController.onDragEnd();
- mBubbleBarViewController.onDragRelease(bubbleView, mReleasedLocation);
+ mBubbleBarViewController.onBubbleDragRelease(mReleasedLocation);
}
@Override
@@ -124,7 +124,7 @@
@Override
void onDragEnd() {
mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
- mBubbleBarViewController.onDragBubbleEnded();
+ mBubbleBarViewController.onBubbleDragEnd();
mBubblePinController.setListener(null);
}
@@ -192,7 +192,7 @@
bubbleBarView.setIsDragging(false);
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
- mBubbleBarViewController.onDragBubbleBarEnded();
+ mBubbleBarViewController.onBubbleBarDragEnd();
mBubbleBarPinController.setListener(null);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 9c3e8af..773b0b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -30,7 +30,7 @@
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.SettingsAwareViewCapture;
+import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -59,7 +59,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext())
+ mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
.startCapture(getRootView(), ".TaskbarOverlay");
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 2eced74..14d391b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -71,7 +71,7 @@
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
- getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+ getContentAlphaProperty().set(mRecentsView, state.isRecentsViewVisible ? 1f : 0);
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
RECENTS_GRID_PROGRESS.set(mRecentsView,
state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
@@ -109,7 +109,8 @@
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
- boolean exitingOverview = !FeatureFlags.enableSplitContextually() && !toState.overviewUi;
+ boolean exitingOverview =
+ !FeatureFlags.enableSplitContextually() && !toState.isRecentsViewVisible;
if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController()
.createPlaceholderDismissAnim(mLauncher, LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
@@ -124,7 +125,8 @@
);
}
- setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
+ setter.setFloat(mRecentsView, getContentAlphaProperty(),
+ toState.isRecentsViewVisible ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
setter.setFloat(
@@ -145,7 +147,7 @@
private Interpolator getOverviewInterpolator(LauncherState fromState, LauncherState toState) {
return fromState == QUICK_SWITCH_FROM_HOME
? ACCELERATE_DECELERATE
- : toState.overviewUi ? INSTANT : FINAL_FRAME;
+ : toState.isRecentsViewVisible ? INSTANT : FINAL_FRAME;
}
abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b1bb198..e379b2a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -103,7 +103,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import com.android.app.viewcapture.SettingsAwareViewCapture;
+import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
@@ -194,9 +194,12 @@
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
import com.android.systemui.unfold.updates.RotationChangeProvider;
+import kotlin.Unit;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -586,6 +589,7 @@
} else {
getStateManager().moveToRestState();
}
+ return Unit.INSTANCE;
});
} else {
getStateManager().goToState(NORMAL);
@@ -668,7 +672,7 @@
addMultiWindowModeChangedListener(mDepthController);
initUnfoldTransitionProgressProvider();
if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- mViewCapture = SettingsAwareViewCapture.getInstance(this).startCapture(getWindow());
+ mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
}
getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE);
QuickstepOnboardingPrefs.setup(this);
@@ -1044,6 +1048,7 @@
getMainExecutor(),
getMainThreadHandler(),
/* backgroundExecutor= */ UI_HELPER_EXECUTOR,
+ /* bgHandler= */ UI_HELPER_EXECUTOR.getHandler(),
/* tracingTagPrefix= */ "launcher",
getSystemService(DisplayManager.class)
);
@@ -1063,7 +1068,7 @@
}
private void initUnfoldAnimationController(UnfoldTransitionProgressProvider progressProvider,
- RotationChangeProvider rotationChangeProvider) {
+ @UnfoldMain RotationChangeProvider rotationChangeProvider) {
mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
/* launcher= */ this,
getWindowManager(),
@@ -1477,4 +1482,9 @@
}
return super.onCreateView(parent, name, context, attrs);
}
+
+ @Override
+ public boolean isRecentsViewVisible() {
+ return getStateManager().getState().isRecentsViewVisible;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index e02ec41..235ec7b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -67,7 +67,7 @@
@Override
public void setState(@NonNull LauncherState state) {
super.setState(state);
- if (state.overviewUi) {
+ if (state.isRecentsViewVisible) {
mRecentsView.updateEmptyMessage();
} else {
mRecentsView.resetTaskVisuals();
@@ -76,7 +76,7 @@
mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
// In Overview, we may be layering app surfaces behind Launcher, so we need to notify
// DepthController to prevent optimizations which might occlude the layers behind
- mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
+ mLauncher.getDepthController().setHasContentBehindLauncher(state.isRecentsViewVisible);
PendingAnimation builder =
new PendingAnimation(state.getTransitionDuration(mLauncher, true));
@@ -89,7 +89,7 @@
@NonNull StateAnimationConfig config, @NonNull PendingAnimation builder) {
super.setStateWithAnimationInternal(toState, config, builder);
- if (toState.overviewUi) {
+ if (toState.isRecentsViewVisible) {
// While animating into recents, update the visible task data as needed
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
@@ -107,7 +107,8 @@
// In Overview, we may be layering app surfaces behind Launcher, so we need to notify
// DepthController to prevent optimizations which might occlude the layers behind
builder.addListener(AnimatorListeners.forSuccessCallback(() ->
- mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
+ mLauncher.getDepthController().setHasContentBehindLauncher(
+ toState.isRecentsViewVisible)));
handleSplitSelectionState(toState, builder, /* animate */true);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index 535b4c2..146ff3d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -40,7 +40,7 @@
import com.android.quickstep.util.FadeOutRemoteTransition
/** A wrapper for the hidden API calls */
-class SystemApiWrapper(context: Context?) : ApiWrapper(context) {
+open class SystemApiWrapper(context: Context?) : ApiWrapper(context) {
override fun getPersons(si: ShortcutInfo) = si.persons ?: Utilities.EMPTY_PERSON_ARRAY
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
index 3881e9a..dc6365b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
@@ -16,14 +16,28 @@
package com.android.launcher3.uioverrides.flags
+import android.app.PendingIntent
+import android.app.blob.BlobHandle.createWithSha256
+import android.app.blob.BlobStoreManager
import android.content.Context
+import android.content.IIntentReceiver
+import android.content.IIntentSender.Stub
import android.content.Intent
+import android.content.Intent.ACTION_CREATE_DOCUMENT
+import android.content.Intent.ACTION_OPEN_DOCUMENT
import android.content.pm.PackageManager
import android.net.Uri
+import android.os.Bundle
+import android.os.IBinder
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream
import android.provider.DeviceConfig
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+import android.provider.Settings.Secure
import android.text.Html
import android.util.AttributeSet
+import android.util.Base64
+import android.util.Base64.NO_PADDING
+import android.util.Base64.NO_WRAP
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import android.widget.Toast
@@ -33,11 +47,32 @@
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceViewHolder
import androidx.preference.SwitchPreference
+import com.android.launcher3.AutoInstallsLayout
import com.android.launcher3.ExtendedEditText
+import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG
import com.android.launcher3.R
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.proxy.ProxyActivityStarter
import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher
+import com.android.launcher3.shortcuts.ShortcutKey
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR
+import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT
import com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_COUNT
import com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN
@@ -45,12 +80,17 @@
import com.android.launcher3.util.OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN
import com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP
import com.android.launcher3.util.PluginManagerWrapper
+import com.android.launcher3.util.StartActivityParams
+import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.util.DeviceConfigHelper
import com.android.quickstep.util.DeviceConfigHelper.Companion.NAMESPACE_LAUNCHER
import com.android.quickstep.util.DeviceConfigHelper.DebugInfo
import com.android.systemui.shared.plugins.PluginEnabler
import com.android.systemui.shared.plugins.PluginPrefs
+import java.io.OutputStreamWriter
+import java.security.MessageDigest
import java.util.Locale
+import java.util.concurrent.Executor
/** Helper class to generate UI for Device Config */
class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, attr) {
@@ -67,6 +107,9 @@
(holder.findViewById(R.id.filter_box) as TextView?)?.doAfterTextChanged {
val query: String = it.toString().lowercase(Locale.getDefault()).replace("_", " ")
filterPreferences(query, this)
+
+ // Always keep myself visible
+ this@DevOptionsUiHelper.isVisible = true
}
}
@@ -97,6 +140,7 @@
}
addIntentTargets()
addOnboardingPrefsCategory()
+ addLayoutSharePref()
}
private fun newCategory(titleText: String, subTitleText: String? = null) =
@@ -359,7 +403,7 @@
Preference(context).also {
it.title = title
it.summary = "Tap to reset"
- setOnPreferenceClickListener { _ ->
+ it.setOnPreferenceClickListener { _ ->
LauncherPrefs.getPrefs(context)
.edit()
.apply { keys.forEach { key -> remove(key) } }
@@ -370,6 +414,137 @@
}
)
+ private fun addLayoutSharePref() {
+ val model = LauncherAppState.getInstance(context).model
+ val category = newCategory("Workspace grid layout")
+ Preference(context).apply {
+ title = "Export"
+ intent =
+ createUriPickerIntent(ACTION_CREATE_DOCUMENT, MAIN_EXECUTOR) { uri ->
+ model.enqueueModelUpdateTask { _, dataModel, _ ->
+ val builder = LauncherLayoutBuilder()
+ dataModel.workspaceItems.forEach { info ->
+ val loc =
+ when (info.container) {
+ CONTAINER_DESKTOP ->
+ builder.atWorkspace(info.cellX, info.cellY, info.screenId)
+ CONTAINER_HOTSEAT -> builder.atHotseat(info.screenId)
+ else -> return@forEach
+ }
+ loc.addItem(info)
+ }
+ dataModel.appWidgets.forEach { info ->
+ builder.atWorkspace(info.cellX, info.cellY, info.screenId).addItem(info)
+ }
+
+ context.contentResolver.openOutputStream(uri).use { os ->
+ builder.build(OutputStreamWriter(os))
+ }
+
+ MAIN_EXECUTOR.execute {
+ Toast.makeText(context, "File saved", Toast.LENGTH_LONG).show()
+ }
+ }
+ }
+ category.addPreference(this)
+ }
+
+ Preference(context).apply {
+ title = "Import"
+ intent =
+ createUriPickerIntent(ACTION_OPEN_DOCUMENT, ORDERED_BG_EXECUTOR) { uri ->
+ val resolver = context.contentResolver
+ val data =
+ resolver.openInputStream(uri).use { stream ->
+ stream?.readAllBytes() ?: return@createUriPickerIntent
+ }
+
+ val digest = MessageDigest.getInstance("SHA-256").digest(data)
+ val handle = createWithSha256(digest, LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG)
+ val blobManager = context.getSystemService(BlobStoreManager::class.java)!!
+
+ blobManager.openSession(blobManager.createSession(handle)).use { session ->
+ AutoCloseOutputStream(session.openWrite(0, -1)).use { it.write(data) }
+ session.allowPublicAccess()
+
+ session.commit(ORDERED_BG_EXECUTOR) {
+ val key = Base64.encodeToString(digest, NO_WRAP or NO_PADDING)
+ Secure.putString(resolver, LAYOUT_DIGEST_KEY, key)
+
+ MODEL_EXECUTOR.submit { model.modelDbController.createEmptyDB() }.get()
+ MAIN_EXECUTOR.submit { model.forceReload() }.get()
+ MODEL_EXECUTOR.submit {}.get()
+ Secure.putString(resolver, LAYOUT_DIGEST_KEY, null)
+ }
+ }
+ }
+ category.addPreference(this)
+ }
+ }
+
+ private fun LauncherLayoutBuilder.ItemTarget.addItem(info: ItemInfo) {
+ val userType: String? =
+ when (UserCache.INSTANCE.get(context).getUserInfo(info.user).type) {
+ UserIconInfo.TYPE_WORK -> AutoInstallsLayout.USER_TYPE_WORK
+ UserIconInfo.TYPE_CLONED -> AutoInstallsLayout.USER_TYPE_CLONED
+ else -> null
+ }
+ when (info.itemType) {
+ ITEM_TYPE_APPLICATION ->
+ info.targetComponent?.let { c -> putApp(c.packageName, c.className, userType) }
+ ITEM_TYPE_DEEP_SHORTCUT ->
+ ShortcutKey.fromItemInfo(info).let { key ->
+ putShortcut(key.packageName, key.id, userType)
+ }
+ ITEM_TYPE_FOLDER ->
+ (info as FolderInfo).let { folderInfo ->
+ putFolder(folderInfo.title?.toString() ?: "").also { folderBuilder ->
+ folderInfo.getContents().forEach { folderContent ->
+ folderBuilder.addItem(folderContent)
+ }
+ }
+ }
+ ITEM_TYPE_APPWIDGET ->
+ putWidget(
+ (info as LauncherAppWidgetInfo).providerName.packageName,
+ info.providerName.className,
+ info.spanX,
+ info.spanY,
+ userType
+ )
+ }
+ }
+
+ private fun createUriPickerIntent(
+ action: String,
+ executor: Executor,
+ callback: (uri: Uri) -> Unit
+ ): Intent {
+ val pendingIntent =
+ PendingIntent(
+ object : Stub() {
+ override fun send(
+ code: Int,
+ intent: Intent,
+ resolvedType: String?,
+ allowlistToken: IBinder?,
+ finishedReceiver: IIntentReceiver?,
+ requiredPermission: String?,
+ options: Bundle?
+ ) {
+ intent.data?.let { uri -> executor.execute { callback(uri) } }
+ }
+ }
+ )
+ val params = StartActivityParams(pendingIntent, 0)
+ params.intent =
+ Intent(action)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("text/xml")
+ .putExtra(Intent.EXTRA_TITLE, "launcher_grid.xml")
+ return ProxyActivityStarter.getLaunchIntent(context, params)
+ }
+
private inner class CustomSwitchPref(
private val bindCallback: (holder: PreferenceViewHolder, pref: SwitchPreference) -> Unit
) : SwitchPreference(context) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 7fa121d..2625646 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -34,7 +34,7 @@
*/
public class BackgroundAppState extends OverviewState {
- private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI
+ private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE
| FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS;
public BackgroundAppState(int id) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 3c291e6..932d241 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -32,7 +32,7 @@
public class OverviewModalTaskState extends OverviewState {
private static final int STATE_FLAGS =
- FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE;
+ FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE | FLAG_WORKSPACE_INACCESSIBLE;
public OverviewModalTaskState(int id) {
super(id, LAUNCHER_STATE_OVERVIEW, STATE_FLAGS);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d0eef8e..7173298 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -47,7 +47,7 @@
protected static final Rect sTempRect = new Rect();
private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
- | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE
+ | FLAG_DISABLE_RESTORE | FLAG_RECENTS_VIEW_VISIBLE | FLAG_WORKSPACE_INACCESSIBLE
| FLAG_CLOSE_POPUPS;
public OverviewState(int id) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 3ed2d0b..11e0ed5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -118,7 +118,7 @@
if (!cameFromNavBar) {
return false;
}
- if (mStartState.overviewUi || mStartState == ALL_APPS) {
+ if (mStartState.isRecentsViewVisible || mStartState == ALL_APPS) {
return true;
}
int typeToClose = TYPE_ALL & ~TYPE_ALL_APPS_EDU;
@@ -145,7 +145,7 @@
private void initCurrentAnimation() {
long accuracy = (long) (getShiftRange() * 2);
final PendingAnimation builder = new PendingAnimation(accuracy);
- if (mStartState.overviewUi) {
+ if (mStartState.isRecentsViewVisible) {
RecentsView recentsView = mLauncher.getOverviewPanel();
AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
builder);
@@ -194,7 +194,7 @@
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.switchToScreenshot(null,
() -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
- if (mStartState.overviewUi) {
+ if (mStartState.isRecentsViewVisible) {
Runnable onReachedHome = () -> {
StateManager.StateListener<LauncherState> listener =
new StateManager.StateListener<>() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 1a98db1..93e4fbd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -151,7 +151,7 @@
int sysuiFlags = 0;
TaskView tv = mOverviewPanel.getTaskViewAt(0);
if (tv != null) {
- sysuiFlags = tv.getFirstThumbnailView().getSysUiStatusNavFlags();
+ sysuiFlags = tv.getFirstThumbnailViewDeprecated().getSysUiStatusNavFlags();
}
mLauncher.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, sysuiFlags);
} else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 26b528c..4bc3c16 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -252,7 +252,7 @@
mTaskBeingDragged, maxDuration, currentInterpolator);
// Since the thumbnail is what is filling the screen, based the end displacement on it.
- View thumbnailView = mTaskBeingDragged.getFirstThumbnailView();
+ View thumbnailView = mTaskBeingDragged.getFirstThumbnailViewDeprecated();
mTempCords[1] = orientationHandler.getSecondaryDimension(thumbnailView);
dl.getDescendantCoordRelativeToSelf(thumbnailView, mTempCords);
mEndDisplacement = secondaryLayerDimension - mTempCords[1];
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 28ae3d2..463222d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -107,7 +107,6 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
-import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.taskbar.TaskbarThresholdUtils;
import com.android.launcher3.taskbar.TaskbarUIController;
@@ -152,6 +151,8 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -924,7 +925,7 @@
TaskView runningTask = mRecentsView.getRunningTaskView();
TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
int centermostTaskFlags = centermostTask == null ? 0
- : centermostTask.getFirstThumbnailView().getSysUiStatusNavFlags();
+ : centermostTask.getFirstThumbnailViewDeprecated().getSysUiStatusNavFlags();
boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
boolean quickswitchThresholdPassed = centermostTask != runningTask;
@@ -1173,12 +1174,6 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
- DesktopVisibilityController desktopVisibilityController =
- mContainerInterface.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- // Notify the SysUI to stash desktop apps if they are visible
- desktopVisibilityController.onHomeActionTriggered();
- }
break;
case RECENTS:
mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
@@ -1474,14 +1469,15 @@
default:
event = IGNORE;
}
- StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
+ StatsLogger logger = StatsLogManager.newInstance(
+ mContainer != null ? mContainer.asContext() : mContext).logger()
.withSrcState(LAUNCHER_STATE_BACKGROUND)
.withDstState(endTarget.containerType)
.withInputType(mGestureState.isTrackpadGesture()
? SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TRACKPAD
: SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TOUCH);
if (targetTask != null) {
- logger.withItemInfo(targetTask.getItemInfo());
+ logger.withItemInfo(targetTask.getFirstItemInfo());
}
int pageIndex = endTarget == LAST_TASK || mRecentsView == null
@@ -2337,6 +2333,7 @@
mRecentsAnimationController.finish(true /* toRecents */, null);
}
}
+ return Unit.INSTANCE;
}, true /* freezeTaskList */);
} else {
mContainerInterface.onLaunchTaskFailed();
@@ -2426,7 +2423,8 @@
RemoteAnimationTarget taskTarget = taskTargetOptional.get();
TaskView taskView = mRecentsView == null
? null : mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
- if (taskView == null || !taskView.getFirstThumbnailView().shouldShowSplashView()) {
+ if (taskView == null
+ || !taskView.getFirstThumbnailViewDeprecated().shouldShowSplashView()) {
ActiveGestureLog.INSTANCE.addLog("Invalid task view splash state");
finishRecentsAnimationOnTasksAppeared(null /* onFinishComplete */);
return;
diff --git a/quickstep/src/com/android/quickstep/DesktopModeStatus.java b/quickstep/src/com/android/quickstep/DesktopModeStatus.java
deleted file mode 100644
index b1aae16..0000000
--- a/quickstep/src/com/android/quickstep/DesktopModeStatus.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import android.content.Context;
-import android.os.SystemProperties;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
-
-// TODO(b/335401172): Explore unifying logic across core and shell
-public class DesktopModeStatus {
-
- /**
- * Flag to indicate whether to restrict desktop mode to supported devices.
- */
- private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
-
- /**
- * Return {@code true} if desktop mode should be restricted to supported devices.
- */
- @VisibleForTesting
- public static boolean enforceDeviceRestrictions() {
- return ENFORCE_DEVICE_RESTRICTIONS;
- }
-
- /**
- * Return {@code true} if the current device supports desktop mode.
- */
- @VisibleForTesting
- public static boolean isDesktopModeSupported(Context context) {
- return context.getResources().getBoolean(
- com.android.internal.R.bool.config_isDesktopModeSupported);
- }
-
- /**
- * Return {@code true} if desktop mode can be entered on the current device.
- */
- public static boolean canEnterDesktopMode(Context context) {
- return Flags.enableDesktopWindowingMode()
- && (!enforceDeviceRestrictions() || isDesktopModeSupported(context));
- }
-}
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index e33ef7f..50a06fc 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -24,28 +24,29 @@
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskView.TaskContainer
+import com.android.wm.shell.shared.DesktopModeStatus
/** A menu item, "Desktop", that allows the user to bring the current app into Desktop Windowing. */
class DesktopSystemShortcut(
container: RecentsViewContainer,
- private val mTaskContainer: TaskContainer,
+ private val taskContainer: TaskContainer,
abstractFloatingViewHelper: AbstractFloatingViewHelper
) :
SystemShortcut<RecentsViewContainer>(
R.drawable.ic_caption_desktop_button_foreground,
R.string.recent_task_option_desktop,
container,
- mTaskContainer.itemInfo,
- mTaskContainer.taskView,
+ taskContainer.itemInfo,
+ taskContainer.taskView,
abstractFloatingViewHelper
) {
override fun onClick(view: View) {
dismissTaskMenuView()
- val recentsView = mTarget!!.getOverviewPanel<RecentsView<*, *>>()
- recentsView.moveTaskToDesktop(mTaskContainer) {
+ val recentsView = mTarget.getOverviewPanel<RecentsView<*, *>>()
+ recentsView.moveTaskToDesktop(taskContainer) {
mTarget.statsLogManager
.logger()
- .withItemInfo(mTaskContainer.itemInfo)
+ .withItemInfo(taskContainer.itemInfo)
.log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 7655c59..811b9fd 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -192,7 +192,7 @@
public RecentsView getVisibleRecentsView() {
QuickstepLauncher launcher = getVisibleLauncher();
RecentsView recentsView =
- launcher != null && launcher.getStateManager().getState().overviewUi
+ launcher != null && launcher.getStateManager().getState().isRecentsViewVisible
? launcher.getOverviewPanel() : null;
if (recentsView == null || (!launcher.hasBeenResumed()
&& recentsView.getRunningTaskViewId() == -1)) {
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 6a9f509..080e03a 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -304,9 +304,7 @@
// Disable if swiping to PIP
return null;
}
- if (sourceTaskView == null
- || sourceTaskView.getFirstTask() == null
- || sourceTaskView.getFirstTask().key.getComponent() == null) {
+ if (sourceTaskView == null || sourceTaskView.getFirstTask().key.getComponent() == null) {
// Disable if it's an invalid task
return null;
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 68923ee..3091e3d 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -16,6 +16,9 @@
package com.android.quickstep;
import static com.android.launcher3.PagedView.INVALID_PAGE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -34,6 +37,8 @@
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.RunnableList;
@@ -171,7 +176,7 @@
RunnableList callbackList = null;
if (taskView != null) {
mWaitForToggleCommandComplete = true;
- taskView.setEndQuickswitchCuj(true);
+ taskView.setEndQuickSwitchCuj(true);
callbackList = taskView.launchTasks();
}
@@ -285,6 +290,7 @@
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
updateRecentsViewFocus(cmd);
+ logShowOverviewFrom(cmd.type);
}
@Override
public void onAnimationEnd(Animator animation) {
@@ -319,6 +325,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
updateRecentsViewFocus(cmd);
+ logShowOverviewFrom(cmd.type);
activityInterface.runOnInitBackgroundStateUI(() ->
interactionHandler.onGestureEnded(0, new PointF()));
cmd.removeListener(this);
@@ -420,6 +427,33 @@
return true;
}
+ private <T extends StatefulActivity<?> & RecentsViewContainer>
+ void logShowOverviewFrom(int cmdType) {
+ BaseActivityInterface<?, T> activityInterface =
+ mOverviewComponentObserver.getActivityInterface();
+ var container = activityInterface.getCreatedContainer();
+ if (container != null) {
+ StatsLogManager.LauncherEvent event;
+ switch (cmdType) {
+ case TYPE_SHOW -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
+ case TYPE_HIDE ->
+ event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
+ case TYPE_TOGGLE -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
+ default -> {
+ return;
+ }
+ }
+
+ StatsLogManager.newInstance(container.asContext())
+ .logger()
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(
+ LauncherAtom.TaskSwitcherContainer.getDefaultInstance())
+ .build())
+ .log(event);
+ }
+ }
+
public void dump(PrintWriter pw) {
pw.println("OverviewCommandHelper:");
pw.println(" mPendingCommands=" + mPendingCommands.size());
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index f57f4c8..97a0b3f 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -33,7 +33,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.app.ActivityOptions;
-import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
@@ -507,4 +506,9 @@
public TISBindHelper getTISBindHelper() {
return mTISBindHelper;
}
+
+ @Override
+ public boolean isRecentsViewVisible() {
+ return getStateManager().getState().isRecentsViewVisible();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index bea6d0f..0ac3ec7 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -761,12 +761,12 @@
/**
* Tells SysUI to show the bubble with the provided key.
* @param key the key of the bubble to show.
- * @param bubbleBarBounds bounds of the bubble bar in display coordinates
+ * @param top top coordinate of bubble bar on screen
*/
- public void showBubble(String key, Rect bubbleBarBounds) {
+ public void showBubble(String key, int top) {
if (mBubbles != null) {
try {
- mBubbles.showBubble(key, bubbleBarBounds);
+ mBubbles.showBubble(key, top);
} catch (RemoteException e) {
Log.w(TAG, "Failed call showBubble");
}
@@ -774,19 +774,6 @@
}
/**
- * Tells SysUI to remove the bubble with the provided key.
- * @param key the key of the bubble to show.
- */
- public void removeBubble(String key) {
- if (mBubbles == null) return;
- try {
- mBubbles.removeBubble(key);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call removeBubble");
- }
- }
-
- /**
* Tells SysUI to remove all bubbles.
*/
public void removeAllBubbles() {
@@ -828,19 +815,33 @@
/**
* Tells SysUI when the bubble stops being dragged.
* Should be called only when the bubble bar is expanded.
- * @param bubbleKey key of the bubble being dragged
+ *
* @param location location of the bubble bar
+ * @param top new top coordinate for bubble bar on screen
*/
- public void stopBubbleDrag(@Nullable String bubbleKey, BubbleBarLocation location) {
+ public void stopBubbleDrag(BubbleBarLocation location, int top) {
if (mBubbles == null) return;
try {
- mBubbles.stopBubbleDrag(bubbleKey, location);
+ mBubbles.stopBubbleDrag(location, top);
} catch (RemoteException e) {
Log.w(TAG, "Failed call stopBubbleDrag");
}
}
/**
+ * Tells SysUI to dismiss the bubble with the provided key.
+ * @param key the key of the bubble to dismiss.
+ */
+ public void dragBubbleToDismiss(String key) {
+ if (mBubbles == null) return;
+ try {
+ mBubbles.dragBubbleToDismiss(key);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call dragBubbleToDismiss");
+ }
+ }
+
+ /**
* Tells SysUI to show user education relative to the reference point provided.
* @param position the bubble bar top center position in Screen coordinates.
*/
@@ -865,16 +866,17 @@
}
/**
- * Tells SysUI the bounds for the bubble bar
- * @param bubbleBarBounds bounds of the bubble bar in display coordinates
+ * Tells SysUI the top coordinate of bubble bar on screen
+ *
+ * @param topOnScreen top coordinate for bubble bar on screen
*/
- public void setBubbleBarBounds(Rect bubbleBarBounds) {
+ public void updateBubbleBarTopOnScreen(int topOnScreen) {
try {
if (mBubbles != null) {
- mBubbles.setBubbleBarBounds(bubbleBarBounds);
+ mBubbles.updateBubbleBarTopOnScreen(topOnScreen);
}
} catch (RemoteException e) {
- Log.w(TAG, "Failed call setBubbleBarBounds");
+ Log.w(TAG, "Failed call updateBubbleBarTopOnScreen");
}
}
@@ -1443,28 +1445,6 @@
}
}
- /** Call shell to stash desktop apps */
- public void stashDesktopApps(int displayId) {
- if (mDesktopMode != null) {
- try {
- mDesktopMode.stashDesktopApps(displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call stashDesktopApps", e);
- }
- }
- }
-
- /** 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);
- }
- }
- }
-
/**
* If task with the given id is on the desktop, bring it to front
*/
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 1a46fb6..b183ae3 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -80,8 +80,9 @@
return shortcuts;
}
- public TaskOverlay createOverlay(TaskThumbnailViewDeprecated thumbnailView) {
- return new TaskOverlay(thumbnailView);
+ /** Creates a {@link TaskOverlay} associated with the provide {@link TaskContainer}. */
+ public TaskOverlay<?> createOverlay(TaskContainer taskContainer) {
+ return new TaskOverlay<>(taskContainer);
}
/**
@@ -124,28 +125,29 @@
public static class TaskOverlay<T extends OverviewActionsView> {
protected final Context mApplicationContext;
- protected final TaskThumbnailViewDeprecated mThumbnailView;
+ protected final TaskContainer mTaskContainer;
private T mActionsView;
protected ImageActionsApi mImageApi;
- protected TaskOverlay(TaskThumbnailViewDeprecated taskThumbnailViewDeprecated) {
- mApplicationContext = taskThumbnailViewDeprecated.getContext().getApplicationContext();
- mThumbnailView = taskThumbnailViewDeprecated;
+ protected TaskOverlay(TaskContainer taskContainer) {
+ mApplicationContext = taskContainer.getTaskView().getContext().getApplicationContext();
+ mTaskContainer = taskContainer;
mImageApi = new ImageActionsApi(
- mApplicationContext, mThumbnailView::getThumbnail);
+ mApplicationContext, mTaskContainer.getThumbnailViewDeprecated()::getThumbnail);
}
protected T getActionsView() {
if (mActionsView == null) {
- mActionsView = BaseActivity.fromContext(mThumbnailView.getContext()).findViewById(
+ mActionsView = BaseActivity.fromContext(
+ mTaskContainer.getThumbnailViewDeprecated().getContext()).findViewById(
R.id.overview_actions_view);
}
return mActionsView;
}
public TaskThumbnailViewDeprecated getThumbnailView() {
- return mThumbnailView;
+ return mTaskContainer.getThumbnailViewDeprecated();
}
/**
@@ -157,7 +159,8 @@
if (thumbnail != null) {
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
- boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
+ boolean isAllowedByPolicy =
+ mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot();
getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
}
}
@@ -168,7 +171,8 @@
* @param callback callback to run, after switching to screenshot
*/
public void endLiveTileMode(@NonNull Runnable callback) {
- RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
+ RecentsView recentsView =
+ mTaskContainer.getThumbnailViewDeprecated().getTaskView().getRecentsView();
// Task has already been dismissed
if (recentsView == null) return;
recentsView.switchToScreenshot(
@@ -181,8 +185,8 @@
*/
@SuppressLint("NewApi")
protected void saveScreenshot(Task task) {
- if (mThumbnailView.isRealSnapshot()) {
- mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+ if (mTaskContainer.getThumbnailViewDeprecated().isRealSnapshot()) {
+ mImageApi.saveScreenshot(mTaskContainer.getThumbnailViewDeprecated().getThumbnail(),
getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
} else {
showBlockedByPolicyMessage();
@@ -190,14 +194,17 @@
}
protected void enterSplitSelect() {
- RecentsView overviewPanel = mThumbnailView.getTaskView().getRecentsView();
+ RecentsView overviewPanel =
+ mTaskContainer.getThumbnailViewDeprecated().getTaskView().getRecentsView();
// Task has already been dismissed
if (overviewPanel == null) return;
- overviewPanel.initiateSplitSelect(mThumbnailView.getTaskView());
+ overviewPanel.initiateSplitSelect(
+ mTaskContainer.getThumbnailViewDeprecated().getTaskView());
}
protected void saveAppPair() {
- GroupedTaskView taskView = (GroupedTaskView) mThumbnailView.getTaskView();
+ GroupedTaskView taskView =
+ (GroupedTaskView) mTaskContainer.getThumbnailViewDeprecated().getTaskView();
taskView.getRecentsView().getSplitSelectController().getAppPairsController()
.saveAppPair(taskView);
}
@@ -243,10 +250,11 @@
*/
public Rect getTaskSnapshotBounds() {
int[] location = new int[2];
- mThumbnailView.getLocationOnScreen(location);
+ mTaskContainer.getThumbnailViewDeprecated().getLocationOnScreen(location);
- return new Rect(location[0], location[1], mThumbnailView.getWidth() + location[0],
- mThumbnailView.getHeight() + location[1]);
+ return new Rect(location[0], location[1],
+ mTaskContainer.getThumbnailViewDeprecated().getWidth() + location[0],
+ mTaskContainer.getThumbnailViewDeprecated().getHeight() + location[1]);
}
/**
@@ -256,7 +264,7 @@
*/
@RequiresApi(api = Build.VERSION_CODES.Q)
public Insets getTaskSnapshotInsets() {
- return mThumbnailView.getScaledInsets();
+ return mTaskContainer.getThumbnailViewDeprecated().getScaledInsets();
}
/**
@@ -267,17 +275,21 @@
protected void showBlockedByPolicyMessage() {
ActivityContext activityContext = ActivityContext.lookupContext(
- mThumbnailView.getContext());
+ mTaskContainer.getThumbnailViewDeprecated().getContext());
String message = activityContext.getStringCache() != null
? activityContext.getStringCache().disabledByAdminMessage
- : mThumbnailView.getContext().getString(R.string.blocked_by_policy);
+ : mTaskContainer.getThumbnailViewDeprecated().getContext().getString(
+ R.string.blocked_by_policy);
- Snackbar.show(BaseActivity.fromContext(mThumbnailView.getContext()), message, null);
+ Snackbar.show(BaseActivity.fromContext(
+ mTaskContainer.getThumbnailViewDeprecated().getContext()), message, null);
}
/** Called when the snapshot has updated its full screen drawing parameters. */
- public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
- }
+ public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {}
+
+ /** Sets visibility for the overlay associated elements. */
+ public void setVisibility(int visibility) {}
private class ScreenshotSystemShortcut extends SystemShortcut {
@@ -292,7 +304,8 @@
@Override
public void onClick(View view) {
- saveScreenshot(mThumbnailView.getTaskView().getFirstTask());
+ saveScreenshot(
+ mTaskContainer.getThumbnailViewDeprecated().getTaskView().getFirstTask());
dismissTaskMenuView();
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index a53d91f..4b5c826 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -42,10 +42,8 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -130,7 +128,8 @@
public SplitSelectSystemShortcut(RecentsViewContainer container, TaskView taskView,
SplitPositionOption option) {
- super(option.iconResId, option.textResId, container, taskView.getItemInfo(), taskView);
+ super(option.iconResId, option.textResId, container, taskView.getFirstItemInfo(),
+ taskView);
mTaskView = taskView;
mSplitPositionOption = option;
}
@@ -151,8 +150,8 @@
public SaveAppPairSystemShortcut(RecentsViewContainer container, GroupedTaskView taskView,
int iconResId) {
- super(iconResId, R.string.save_app_pair, container,
- taskView.getItemInfo(), taskView);
+ super(iconResId, R.string.save_app_pair, container, taskView.getFirstItemInfo(),
+ taskView);
mTaskView = taskView;
}
@@ -182,7 +181,7 @@
mHandler = new Handler(Looper.getMainLooper());
mTaskView = taskContainer.getTaskView();
mRecentsView = container.getOverviewPanel();
- mThumbnailView = taskContainer.getThumbnailView();
+ mThumbnailView = taskContainer.getThumbnailViewDeprecated();
}
@Override
@@ -242,7 +241,7 @@
overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
- mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+ mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getFirstItemInfo())
.log(mLauncherEvent);
}
}
@@ -426,7 +425,7 @@
mTaskView.getFirstTask().key.id);
}
dismissTaskMenuView();
- mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+ mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getFirstItemInfo())
.log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
}
}
@@ -463,8 +462,7 @@
boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
// Extra conditions if it's not grid-only overview
if (!isGridOnlyOverview) {
- RecentsOrientedState orientedState =
- taskContainer.getTaskView().getRecentsView().getPagedViewOrientedState();
+ RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
&& orientedState.getTouchRotation() != ROTATION_0;
if (!isFakeLandscape) {
@@ -472,10 +470,8 @@
}
}
- SystemShortcut screenshotShortcut =
- taskContainer.getThumbnailView().getTaskOverlay()
- .getScreenshotShortcut(container, taskContainer.getItemInfo(),
- taskContainer.getTaskView());
+ SystemShortcut screenshotShortcut = taskContainer.getOverlay().getScreenshotShortcut(
+ container, taskContainer.getItemInfo(), taskContainer.getTaskView());
return createSingletonShortcutList(screenshotShortcut);
}
@@ -493,8 +489,7 @@
boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
// Extra conditions if it's not grid-only overview
if (!isGridOnlyOverview) {
- RecentsOrientedState orientedState =
- taskContainer.getTaskView().getRecentsView().getPagedViewOrientedState();
+ RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
&& orientedState.getTouchRotation() != ROTATION_0;
if (!isFakeLandscape) {
@@ -507,9 +502,8 @@
}
SystemShortcut modalStateSystemShortcut =
- taskContainer.getThumbnailView().getTaskOverlay()
- .getModalStateSystemShortcut(
- taskContainer.getItemInfo(), taskContainer.getTaskView());
+ taskContainer.getOverlay().getModalStateSystemShortcut(
+ taskContainer.getItemInfo(), taskContainer.getTaskView());
return createSingletonShortcutList(modalStateSystemShortcut);
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index f6eef62..7ebb767 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -153,7 +153,7 @@
Preconditions.assertUIThread();
boolean lowResolution = !mHighResLoadingState.isEnabled();
- if (task.thumbnail != null && task.thumbnail.thumbnail != null
+ if (task.thumbnail != null && task.thumbnail.getThumbnail() != null
&& (!task.thumbnail.reducedResolution || lowResolution)) {
// Nothing to load, the thumbnail is already high-resolution or matches what the
// request, so just callback
@@ -189,7 +189,7 @@
Preconditions.assertUIThread();
ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
- if (cachedThumbnail != null && cachedThumbnail.thumbnail != null
+ if (cachedThumbnail != null && cachedThumbnail.getThumbnail() != null
&& (!cachedThumbnail.reducedResolution || lowResolution)) {
// Already cached, lets use that thumbnail
callback.accept(cachedThumbnail);
@@ -200,7 +200,7 @@
() -> {
ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance()
.getTaskThumbnail(key.id, lowResolution);
- return thumbnailData.thumbnail != null ? thumbnailData
+ return thumbnailData.getThumbnail() != null ? thumbnailData
: ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id);
},
MAIN_EXECUTOR,
@@ -210,7 +210,7 @@
if (enableGridOnlyOverview() && result.reducedResolution
&& getHighResLoadingState().isEnabled()) {
ThumbnailData newCachedThumbnail = mCache.getAndInvalidateIfModified(key);
- if (newCachedThumbnail != null && newCachedThumbnail.thumbnail != null
+ if (newCachedThumbnail != null && newCachedThumbnail.getThumbnail() != null
&& !newCachedThumbnail.reducedResolution) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index d2560e6..ecd84f8 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -165,8 +165,8 @@
@NonNull RemoteAnimationTarget[] nonAppTargets,
@Nullable DepthController depthController,
PendingAnimation out) {
- boolean isQuickSwitch = v.isEndQuickswitchCuj();
- v.setEndQuickswitchCuj(false);
+ boolean isQuickSwitch = v.isEndQuickSwitchCuj();
+ v.setEndQuickSwitchCuj(false);
final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 644e4f9..94764a5 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -136,7 +136,7 @@
}
private Interpolator getOverviewInterpolator(RecentsState toState) {
- return toState.overviewUi() ? INSTANT : FINAL_FRAME;
+ return toState.isRecentsViewVisible() ? INSTANT : FINAL_FRAME;
}
/**
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index b79586b..096ed2c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -247,7 +247,7 @@
}
// Set border after select mode changes to avoid showing border during state transition
- if (!toState.overviewUi() || toState == MODAL_TASK) {
+ if (!toState.isRecentsViewVisible() || toState == MODAL_TASK) {
setTaskBorderEnabled(false);
}
@@ -267,7 +267,7 @@
setOverviewSelectEnabled(false);
}
- if (finalState.overviewUi() && finalState != MODAL_TASK) {
+ if (finalState.isRecentsViewVisible() && finalState != MODAL_TASK) {
setTaskBorderEnabled(true);
}
@@ -298,7 +298,7 @@
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// Do not let touch escape to siblings below this view.
- return result || mContainer.getStateManager().getState().overviewUi();
+ return result || mContainer.getStateManager().getState().isRecentsViewVisible();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 84937a2..ca9753f 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -26,7 +26,6 @@
import com.android.launcher3.R;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.util.Themes;
-import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
/**
@@ -41,22 +40,23 @@
private static final int FLAG_SHOW_AS_GRID = BaseState.getFlag(4);
private static final int FLAG_SCRIM = BaseState.getFlag(5);
private static final int FLAG_LIVE_TILE = BaseState.getFlag(6);
- private static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+ private static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(7);
private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8);
public static final RecentsState DEFAULT = new RecentsState(0,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
- | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
+ | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
public static final RecentsState MODAL_TASK = new ModalState(1,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
- | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
+ | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE);
public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
- FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN | FLAG_OVERVIEW_UI
+ FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN
+ | FLAG_RECENTS_VIEW_VISIBLE
| FLAG_TASK_THUMBNAIL_SPLASH);
public static final RecentsState HOME = new RecentsState(3, 0);
public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
- FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS
+ FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_RECENTS_VIEW_VISIBLE | FLAG_CLOSE_POPUPS
| FLAG_DISABLE_RESTORE);
public final int ordinal;
@@ -152,8 +152,8 @@
/**
* True if the state has overview panel visible.
*/
- public boolean overviewUi() {
- return hasFlag(FLAG_OVERVIEW_UI);
+ public boolean isRecentsViewVisible() {
+ return hasFlag(FLAG_RECENTS_VIEW_VISIBLE);
}
private static class ModalState extends RecentsState {
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 3cae4dc..1d4160d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,6 +16,10 @@
package com.android.quickstep.logging;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
import static androidx.core.util.Preconditions.checkNotNull;
import static androidx.core.util.Preconditions.checkState;
@@ -26,10 +30,17 @@
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
+import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
import android.content.Context;
import android.text.TextUtils;
@@ -59,6 +70,7 @@
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LogConfig;
import com.android.launcher3.views.ActivityContext;
@@ -226,10 +238,15 @@
private int mInputType = SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__UNKNOWN;
private Optional<Integer> mFeatures = Optional.empty();
private Optional<String> mPackageName = Optional.empty();
+ /**
+ * Indicates the current rotation of the display. Uses {@link android.view.Surface values.}
+ */
+ private final int mDisplayRotation;
StatsCompatLogger(Context context, ActivityContext activityContext) {
mContext = context;
mActivityContext = Optional.ofNullable(activityContext);
+ mDisplayRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
}
@Override
@@ -502,7 +519,28 @@
getSearchAttributes(atomInfo) /* searchAttributes */,
getAttributes(atomInfo) /* attributes */,
inputType /* input_type */,
- atomInfo.getUserType() /* user_type */);
+ atomInfo.getUserType() /* user_type */,
+ getDisplayRotation() /* display_rotation */,
+ getRecentsOrientationHandler(atomInfo) /* recents_orientation_handler */);
+ }
+
+ private int getDisplayRotation() {
+ return switch (mDisplayRotation) {
+ case ROTATION_90 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_90;
+ case ROTATION_180 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_180;
+ case ROTATION_270 -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_270;
+ default -> LAUNCHER_UICHANGED__DISPLAY_ROTATION__ROTATION_0;
+ };
+ }
+
+ private int getRecentsOrientationHandler(LauncherAtom.ItemInfo itemInfo) {
+ var orientationHandler =
+ itemInfo.getContainerInfo().getTaskSwitcherContainer().getOrientationHandler();
+ return switch (orientationHandler) {
+ case PORTRAIT -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__PORTRAIT;
+ case LANDSCAPE -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__LANDSCAPE;
+ case SEASCAPE -> LAUNCHER_UICHANGED__RECENTS_ORIENTATION_HANDLER__SEASCAPE;
+ };
}
}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 1640104..8f8cc6e 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -42,6 +42,7 @@
import com.android.launcher3.LauncherAnimUtils
import com.android.launcher3.R
import com.android.launcher3.Utilities
+import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer
import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds
import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
@@ -611,6 +612,9 @@
override fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float =
floatingTask.translationY
+ override fun getHandlerTypeForLogging(): TaskSwitcherContainer.OrientationHandler =
+ TaskSwitcherContainer.OrientationHandler.LANDSCAPE
+
/**
* Retrieves split icons position
*
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index 1be908b..f6284d5 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -46,9 +46,12 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.touch.DefaultPagedViewHandler;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.SplitConfigurationOptions;
@@ -802,4 +805,10 @@
? floatingTask.getTranslationX()
: floatingTask.getTranslationY();
}
+
+ @NonNull
+ @Override
+ public LauncherAtom.TaskSwitcherContainer.OrientationHandler getHandlerTypeForLogging() {
+ return LauncherAtom.TaskSwitcherContainer.OrientationHandler.PORTRAIT;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index 6c82890..5bc1be8 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -26,6 +26,7 @@
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.touch.PagedOrientationHandler
import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
@@ -371,6 +372,8 @@
*/
fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float
+ fun getHandlerTypeForLogging(): LauncherAtom.TaskSwitcherContainer.OrientationHandler
+
companion object {
@JvmField val PORTRAIT: RecentsPagedOrientationHandler = PortraitPagedViewHandler()
@JvmField val LANDSCAPE: RecentsPagedOrientationHandler = LandscapePagedViewHandler()
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 5bebf8c..46e4b0c 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -32,6 +32,7 @@
import com.android.launcher3.Flags
import com.android.launcher3.R
import com.android.launcher3.Utilities
+import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
@@ -395,4 +396,8 @@
iconView.layoutParams = layoutParams
}
}
+
+ @Override
+ override fun getHandlerTypeForLogging(): LauncherAtom.TaskSwitcherContainer.OrientationHandler =
+ LauncherAtom.TaskSwitcherContainer.OrientationHandler.SEASCAPE
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index b466f3f..8762976 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -46,7 +46,7 @@
RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
.getOverviewPanel<RecentsView<*, *>>()
.mRecentsViewData,
- (parent as TaskView).mTaskViewData
+ (parent as TaskView).taskViewData
)
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 328a727..f1e2dd7 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -21,8 +21,11 @@
import android.view.ViewGroup;
import android.view.WindowManager;
+import androidx.annotation.MainThread;
+
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.util.HashMap;
@@ -34,7 +37,7 @@
public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
- private final RotationChangeProvider mRotationChangeProvider;
+ @UnfoldMain private final RotationChangeProvider mRotationChangeProvider;
private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
@@ -48,7 +51,7 @@
private Float mLastTransitionProgress = null;
public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager,
- RotationChangeProvider rotationChangeProvider) {
+ @UnfoldMain RotationChangeProvider rotationChangeProvider) {
mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
mRotationChangeProvider = rotationChangeProvider;
@@ -143,8 +146,14 @@
private class UnfoldMoveFromCenterRotationListener implements
RotationChangeProvider.RotationListener {
+ @MainThread
@Override
public void onRotationChanged(@Rotation int newRotation) {
+ onRotationChangedInternal(newRotation);
+ }
+
+ @MainThread
+ private void onRotationChangedInternal(@Rotation int newRotation) {
mMoveFromCenterAnimation.updateDisplayProperties(newRotation);
updateRegisteredViewsIfNeeded();
}
diff --git a/quickstep/src/com/android/quickstep/util/BorderAnimator.kt b/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
index 44eb070..85238ed 100644
--- a/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
+++ b/quickstep/src/com/android/quickstep/util/BorderAnimator.kt
@@ -86,7 +86,7 @@
fun createSimpleBorderAnimator(
@Px borderRadiusPx: Int,
@Px borderWidthPx: Int,
- boundsBuilder: (rect: Rect?) -> Unit,
+ boundsBuilder: (Rect) -> Unit,
targetView: View,
@ColorInt borderColor: Int = DEFAULT_BORDER_COLOR,
appearanceDurationMs: Long = DEFAULT_APPEARANCE_ANIMATION_DURATION_MS,
@@ -250,7 +250,7 @@
/** BorderAnimationParams that simply draws the border outside the bounds of the target view. */
private class SimpleParams(
@Px borderWidthPx: Int,
- boundsBuilder: (rect: Rect?) -> Unit,
+ boundsBuilder: (Rect) -> Unit,
targetView: View,
) : BorderAnimationParams(borderWidthPx, boundsBuilder, targetView) {
override val alignmentAdjustmentInset = 0
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 4474f33..26668c8 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -39,6 +39,7 @@
import com.android.quickstep.util.unfold.PreemptiveUnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -76,7 +77,7 @@
QuickstepLauncher launcher,
WindowManager windowManager,
UnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
- RotationChangeProvider rotationChangeProvider) {
+ @UnfoldMain RotationChangeProvider rotationChangeProvider) {
mLauncher = launcher;
if (FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 40ea70f..8243ede 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -118,8 +118,8 @@
if (container.task.getKey().getId() == splitSelectStateController.initialTaskId) {
val drawable = getDrawable(container.iconView, splitSelectSource)
return SplitAnimInitProps(
- container.thumbnailView,
- container.thumbnailView.thumbnail,
+ container.thumbnailViewDeprecated,
+ container.thumbnailViewDeprecated.thumbnail,
drawable!!,
fadeWithThumbnail = true,
isStagedTask = true,
@@ -137,8 +137,8 @@
taskView.taskContainers.first().let {
val drawable = getDrawable(it.iconView, splitSelectSource)
return SplitAnimInitProps(
- it.thumbnailView,
- it.thumbnailView.thumbnail,
+ it.thumbnailViewDeprecated,
+ it.thumbnailViewDeprecated.thumbnail,
drawable!!,
fadeWithThumbnail = true,
isStagedTask = true,
@@ -182,7 +182,7 @@
taskViewHeight: Int,
isPrimaryTaskSplitting: Boolean
) {
- val thumbnail = taskIdAttributeContainer.thumbnailView
+ val thumbnail = taskIdAttributeContainer.thumbnailViewDeprecated
val iconView: View = taskIdAttributeContainer.iconView.asView()
builder.add(ObjectAnimator.ofFloat(thumbnail, TaskThumbnailViewDeprecated.SPLASH_ALPHA, 1f))
thumbnail.setShowSplashForSplitSelection(true)
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index df1879e..7e7c794 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -983,6 +983,7 @@
void onDestroy() {
SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener(
mSplitSelectListener);
+ mSplitSelectListener = null;
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 85d4f4b..5e42b90 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -136,7 +136,7 @@
RectF startingTaskRect = new RectF();
final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
mLauncher, mLauncher.getDragLayer(),
- controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
+ controller.screenshotTask(runningTaskInfo.taskId).getThumbnail(),
null /* icon */, startingTaskRect);
RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
.getIconCache()
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 9da4985..49f4e5f 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -551,7 +551,7 @@
* TaskView
*/
public float getCurrentCornerRadius() {
- float visibleRadius = mCurrentFullscreenParams.mCurrentDrawnCornerRadius;
+ float visibleRadius = mCurrentFullscreenParams.getCurrentDrawnCornerRadius();
mTempPoint[0] = visibleRadius;
mTempPoint[1] = 0;
mInversePositionMatrix.mapVectors(mTempPoint);
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index 4aea1b8..a740e0b 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -21,6 +21,7 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
@@ -30,8 +31,9 @@
private final QuickstepLauncher mLauncher;
- public UnfoldMoveFromCenterHotseatAnimator(QuickstepLauncher launcher,
- WindowManager windowManager, RotationChangeProvider rotationChangeProvider) {
+ public UnfoldMoveFromCenterHotseatAnimator(
+ QuickstepLauncher launcher, WindowManager windowManager,
+ @UnfoldMain RotationChangeProvider rotationChangeProvider) {
super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 0ec3ae0..6e330bb 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -22,6 +22,7 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
@@ -31,8 +32,9 @@
private final QuickstepLauncher mLauncher;
- public UnfoldMoveFromCenterWorkspaceAnimator(QuickstepLauncher launcher,
- WindowManager windowManager, RotationChangeProvider rotationChangeProvider) {
+ public UnfoldMoveFromCenterWorkspaceAnimator(
+ QuickstepLauncher launcher, WindowManager windowManager,
+ @UnfoldMain RotationChangeProvider rotationChangeProvider) {
super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
deleted file mode 100644
index 6a9a268..0000000
--- a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.app.animation.Interpolators.EMPHASIZED_DECELERATE;
-import static com.android.app.animation.Interpolators.LINEAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-
-/**
- * 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 SHOW_INITIAL_HEIGHT_DP = 7;
- private static final int SHOW_CONTAINER_SCALE_DURATION = 333;
- private static final int SHOW_CONTAINER_ALPHA_DURATION = 83;
- private static final int SHOW_CONTENT_ALPHA_DELAY = 67;
- private static final int SHOW_CONTENT_ALPHA_DURATION = 83;
- private static final int HIDE_DURATION = 83;
-
- private final RecentsViewContainer mContainer;
-
- private View mText;
- private View mCloseButton;
- @Nullable
- private Runnable mOnCloseCallback;
- private AnimatorSet mShowAnimation;
- private Animator mHideAnimation;
-
- 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);
- mContainer = RecentsViewContainer.containerFromContext(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);
- view.show();
- return view;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mText = findViewById(R.id.desktop_app_select_text);
- mCloseButton = findViewById(R.id.close_button);
- mCloseButton.setOnClickListener(v -> {
- if (mHideAnimation == null) {
- hide();
- if (mOnCloseCallback != null) {
- mOnCloseCallback.run();
- }
- }
- });
- }
-
- private void show() {
- mContainer.getDragLayer().addView(this);
-
- // Set up initial values
- getBackground().setAlpha(0);
- mText.setAlpha(0);
- mCloseButton.setAlpha(0);
- int initialHeightPx = Utilities.dpToPx(SHOW_INITIAL_HEIGHT_DP);
- int finalHeight = getResources().getDimensionPixelSize(
- R.dimen.desktop_mode_floating_app_select_height);
- float initialScale = initialHeightPx / (float) finalHeight;
- setScaleY(initialScale);
- setPivotY(0);
-
- // Animate the container
- ValueAnimator containerBackground = ValueAnimator.ofInt(0, 255);
- containerBackground.addUpdateListener(
- animation -> getBackground().setAlpha((Integer) animation.getAnimatedValue()));
- containerBackground.setDuration(SHOW_CONTAINER_ALPHA_DURATION);
- containerBackground.setInterpolator(LINEAR);
-
- ObjectAnimator containerSize = ObjectAnimator.ofFloat(this, SCALE_Y, 1f);
- containerSize.setDuration(SHOW_CONTAINER_SCALE_DURATION);
- containerSize.setInterpolator(EMPHASIZED_DECELERATE);
-
- // Animate the contents
- ObjectAnimator textAlpha = ObjectAnimator.ofFloat(mText, ALPHA, 1);
- ObjectAnimator buttonAlpha = ObjectAnimator.ofFloat(mCloseButton, ALPHA, 1);
- AnimatorSet contentAlpha = new AnimatorSet();
- contentAlpha.playTogether(textAlpha, buttonAlpha);
- contentAlpha.setStartDelay(SHOW_CONTENT_ALPHA_DELAY);
- contentAlpha.setDuration(SHOW_CONTENT_ALPHA_DURATION);
- contentAlpha.setInterpolator(LINEAR);
-
- // Start the animation
- mShowAnimation = new AnimatorSet();
- mShowAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mShowAnimation = null;
- }
- });
- mShowAnimation.playTogether(containerBackground, containerSize, contentAlpha);
- mShowAnimation.start();
- }
-
- /**
- * Hide the floating view
- */
- public void hide() {
- if (mShowAnimation != null) {
- mShowAnimation.cancel();
- }
- mHideAnimation = ObjectAnimator.ofFloat(this, ALPHA, 0);
- mHideAnimation.setDuration(HIDE_DURATION).setInterpolator(LINEAR);
- mHideAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mContainer.getDragLayer().removeView(DesktopAppSelectView.this);
- mHideAnimation = null;
- }
- });
- mHideAnimation.start();
- }
-
- /**
- * Add a callback that is called when close button is clicked
- */
- public void setOnCloseClickCallback(@Nullable Runnable callback) {
- mOnCloseCallback = callback;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 3565174..936f6a1 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -27,219 +27,66 @@
import android.view.View
import android.view.ViewGroup
import androidx.core.view.updateLayoutParams
-import com.android.launcher3.LauncherState
import com.android.launcher3.R
-import com.android.launcher3.Utilities
-import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.TransformingTouchDelegate
import com.android.launcher3.util.ViewPool
+import com.android.launcher3.util.rects.set
import com.android.quickstep.BaseContainerInterface
-import com.android.quickstep.RecentsModel
+import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.util.RecentsOrientedState
import com.android.systemui.shared.recents.model.Task
-import com.android.systemui.shared.recents.model.ThumbnailData
-import com.android.systemui.shared.system.QuickStepContract
-import java.util.function.Consumer
/** TaskView that contains all tasks that are part of the desktop. */
-// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
-class DesktopTaskView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
+class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
TaskView(context, attrs) {
- private val pendingThumbnailRequests = mutableListOf<CancellableTask<*>>()
private val snapshotDrawParams =
object : FullscreenDrawParams(context) {
+ // DesktopTaskView thumbnail's corner radius is independent of fullscreenProgress.
override fun computeTaskCornerRadius(context: Context) =
- QuickStepContract.getWindowCornerRadius(context)
-
- override fun computeWindowCornerRadius(context: Context) =
- QuickStepContract.getWindowCornerRadius(context)
+ computeWindowCornerRadius(context)
}
private val taskThumbnailViewPool =
ViewPool<TaskThumbnailViewDeprecated>(
context,
this,
R.layout.task_thumbnail,
- 10,
- 0 // As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE
)
private val tempPointF = PointF()
private val tempRect = Rect()
private lateinit var backgroundView: View
+ private lateinit var iconView: TaskViewIcon
private var childCountAtInflation = 0
- init {
- mTaskContainers = ArrayList()
- }
-
override fun onFinishInflate() {
super.onFinishInflate()
-
- backgroundView = findViewById(R.id.background)!!
- val topMarginPx = mContainer.deviceProfile.overviewTaskThumbnailTopMarginPx
- backgroundView.updateLayoutParams<LayoutParams> { topMargin = topMarginPx }
-
- val outerRadii = FloatArray(8) { taskCornerRadius }
- backgroundView.background =
- ShapeDrawable(RoundRectShape(outerRadii, null, null)).apply {
- setTint(resources.getColor(android.R.color.system_neutral2_300, context.theme))
- }
-
- val iconBackground = resources.getDrawable(R.drawable.bg_circle, context.theme)
- val icon = resources.getDrawable(R.drawable.ic_desktop, context.theme)
- setIcon(mIconView, LayerDrawable(arrayOf(iconBackground, icon)))
-
- childCountAtInflation = childCount
- }
-
- override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
- if (relativeToDragLayer) {
- mContainer.dragLayer.getDescendantRectRelativeToSelf(backgroundView, bounds)
- } else {
- bounds.set(
- backgroundView.left,
- backgroundView.top,
- backgroundView.right,
- backgroundView.bottom
- )
- }
- }
-
- override fun bind(task: Task, orientedState: RecentsOrientedState) {
- bind(listOf(task), orientedState)
- }
-
- /** Updates this desktop task to the gives task list defined in `tasks` */
- fun bind(tasks: List<Task>, orientedState: RecentsOrientedState) {
- if (DEBUG) {
- val sb = StringBuilder()
- sb.append("bind tasks=").append(tasks.size).append("\n")
- tasks.forEach { sb.append(" key=${it.key}\n") }
- Log.d(TAG, sb.toString())
- }
- cancelPendingLoadTasks()
-
- (mTaskContainers as ArrayList).ensureCapacity(tasks.size)
- tasks.forEachIndexed { index, task ->
- val thumbnailView: TaskThumbnailViewDeprecated
- if (index >= mTaskContainers.size) {
- thumbnailView = taskThumbnailViewPool.view
- // Add thumbnailView from to position after the initial child views.
- addView(
- thumbnailView,
- childCountAtInflation,
- LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
- )
- )
- } else {
- thumbnailView = mTaskContainers[index].thumbnailView
- }
- thumbnailView.bind(task)
- val taskContainer =
- TaskContainer(
- task,
- thumbnailView,
- mIconView,
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- null
- )
- if (index >= mTaskContainers.size) {
- mTaskContainers.add(taskContainer)
- } else {
- mTaskContainers[index] = taskContainer
- }
- }
- while (mTaskContainers.size > tasks.size) {
- mTaskContainers.removeLast().apply {
- removeView(thumbnailView)
- taskThumbnailViewPool.recycle(thumbnailView)
- }
- }
-
- setOrientationState(orientedState)
- }
-
- override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
- cancelPendingLoadTasks()
- if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
- mTaskContainers.forEach {
- if (visible) {
- RecentsModel.INSTANCE.get(context)
- .thumbnailCache
- .updateThumbnailInBackground(it.task) { thumbnailData: ThumbnailData ->
- it.thumbnailView.setThumbnail(it.task, thumbnailData)
+ backgroundView =
+ findViewById<View>(R.id.background)!!.apply {
+ updateLayoutParams<LayoutParams> {
+ topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
+ background =
+ ShapeDrawable(RoundRectShape(FloatArray(8) { taskCornerRadius }, null, null))
+ .apply {
+ setTint(
+ resources.getColor(
+ android.R.color.system_neutral2_300,
+ context.theme
+ )
+ )
}
- ?.apply { pendingThumbnailRequests.add(this) }
- } else {
- it.thumbnailView.setThumbnail(null, null)
- // Reset the task thumbnail ref
- it.task.thumbnail = null
- }
}
- }
- }
-
- // thumbnailView is laid out differently and is handled in onMeasure
- override fun setThumbnailOrientation(orientationState: RecentsOrientedState) {}
-
- override fun cancelPendingLoadTasks() {
- pendingThumbnailRequests.forEach { it.cancel() }
- pendingThumbnailRequests.clear()
- }
-
- override fun launchTaskAnimated(): RunnableList? {
- val recentsView = recentsView ?: return null
- val endCallback = RunnableList()
- val desktopController = recentsView.desktopRecentsController
- if (desktopController != null) {
- desktopController.launchDesktopFromRecents(this) { endCallback.executeAllAndDestroy() }
- Log.d(
- TAG,
- "launchTaskAnimated - launchDesktopFromRecents: ${taskIds.contentToString()}"
- )
- } else {
- Log.d(
- TAG,
- "launchTaskAnimated - recentsController is null: ${taskIds.contentToString()}"
- )
- }
-
- // Callbacks get run from recentsView for case when recents animation already running
- recentsView.addSideTaskLaunchCallback(endCallback)
- return endCallback
- }
-
- override fun launchTask(callback: Consumer<Boolean>, isQuickswitch: Boolean) {
- launchTasks()
- callback.accept(true)
- }
-
- public override fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
- // Sets new thumbnails based on the incoming data and refreshes the rest.
- thumbnailDatas?.let {
- mTaskContainers.forEach {
- val thumbnailData = thumbnailDatas[it.task.key.id]
- if (thumbnailData != null) {
- it.thumbnailView.setThumbnail(it.task, thumbnailData)
- } else {
- // Refresh the rest that were not updated.
- it.thumbnailView.refresh()
- }
+ iconView =
+ getOrInflateIconView(R.id.icon).apply {
+ val iconBackground = resources.getDrawable(R.drawable.bg_circle, context.theme)
+ val icon = resources.getDrawable(R.drawable.ic_desktop, context.theme)
+ setIcon(this, LayerDrawable(arrayOf(iconBackground, icon)))
}
- }
- }
-
- override fun onRecycle() {
- resetPersistentViewTransforms()
- // Clear any references to the thumbnail (it will be re-read either from the cache or the
- // system on next bind)
- mTaskContainers.forEach { it.thumbnailView.setThumbnail(it.task, null) }
- setOverlayEnabled(false)
- onTaskListVisibilityChanged(false)
- visibility = VISIBLE
+ childCountAtInflation = childCount
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -248,14 +95,14 @@
var containerHeight = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(containerWidth, containerHeight)
- if (mTaskContainers.isEmpty()) {
+ if (taskContainers.isEmpty()) {
return
}
- val thumbnailTopMarginPx = mContainer.deviceProfile.overviewTaskThumbnailTopMarginPx
+ val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
containerHeight -= thumbnailTopMarginPx
- BaseContainerInterface.getTaskDimension(mContext, mContainer.deviceProfile, tempPointF)
+ BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
val windowWidth = tempPointF.x.toInt()
val windowHeight = tempPointF.y.toInt()
val scaleWidth = containerWidth / windowWidth.toFloat()
@@ -269,7 +116,7 @@
}
// Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
- mTaskContainers.forEach {
+ taskContainers.forEach {
// Default to quarter of the desktop if we did not get app bounds.
val taskSize =
it.task.appBounds
@@ -281,7 +128,7 @@
}
val thumbWidth = (taskSize.width() * scaleWidth).toInt()
val thumbHeight = (taskSize.height() * scaleHeight).toInt()
- it.thumbnailView.measure(
+ it.thumbnailViewDeprecated.measure(
MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY)
)
@@ -292,8 +139,8 @@
var taskY = (positionInParent.y * scaleHeight).toInt()
// move task down by margin size
taskY += thumbnailTopMarginPx
- it.thumbnailView.x = taskX.toFloat()
- it.thumbnailView.y = taskY.toFloat()
+ it.thumbnailViewDeprecated.x = taskX.toFloat()
+ it.thumbnailViewDeprecated.y = taskY.toFloat()
if (DEBUG) {
Log.d(
TAG,
@@ -304,54 +151,132 @@
}
}
- // TODO(b/330685808) support overlay for Screenshot action
- override fun setOverlayEnabled(overlayEnabled: Boolean) {}
+ override fun onRecycle() {
+ super.onRecycle()
+ visibility = VISIBLE
+ }
- override fun setFullscreenProgress(progress: Float) {
- // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
- val boundProgress = Utilities.boundToRange(progress, 0f, 1f)
- mFullscreenProgress = boundProgress
- mIconView.setVisibility(if (boundProgress < 1) VISIBLE else INVISIBLE)
- // Don't show background while we are transitioning to/from fullscreen
- backgroundView.visibility = if (mFullscreenProgress > 0) INVISIBLE else VISIBLE
- mTaskContainers.forEach {
- it.thumbnailView.taskOverlay.setFullscreenProgress(boundProgress)
+ /** Updates this desktop task to the gives task list defined in `tasks` */
+ fun bind(
+ tasks: List<Task>,
+ orientedState: RecentsOrientedState,
+ taskOverlayFactory: TaskOverlayFactory
+ ) {
+ if (DEBUG) {
+ val sb = StringBuilder()
+ sb.append("bind tasks=").append(tasks.size).append("\n")
+ tasks.forEach { sb.append(" key=${it.key}\n") }
+ Log.d(TAG, sb.toString())
}
- // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
- // oversized and banner would look disproportionately large.
- if (
- mContainer.getOverviewPanel<RecentsView<*, *>>().getStateManager().state !=
- LauncherState.BACKGROUND_APP
- ) {
- setIconsAndBannersTransitionProgress(boundProgress, true)
+ cancelPendingLoadTasks()
+
+ if (!isTaskContainersInitialized()) {
+ taskContainers = arrayListOf()
}
- updateSnapshotRadius()
+ val taskContainers = taskContainers as ArrayList
+ taskContainers.ensureCapacity(tasks.size)
+ tasks.forEachIndexed { index, task ->
+ val thumbnailViewDeprecated: TaskThumbnailViewDeprecated
+ if (index >= taskContainers.size) {
+ thumbnailViewDeprecated = taskThumbnailViewPool.view
+ // Add thumbnailView from to position after the initial child views.
+ addView(
+ thumbnailViewDeprecated,
+ childCountAtInflation,
+ LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ )
+ } else {
+ thumbnailViewDeprecated = taskContainers[index].thumbnailViewDeprecated
+ }
+ val taskContainer =
+ TaskContainer(
+ task,
+ // TODO(b/338360089): Support new TTV for DesktopTaskView
+ thumbnailView = null,
+ thumbnailViewDeprecated,
+ iconView,
+ TransformingTouchDelegate(iconView.asView()),
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+ digitalWellBeingToast = null,
+ showWindowsView = null,
+ taskOverlayFactory
+ )
+ .apply { thumbnailViewDeprecated.bind(task, overlay) }
+ if (index >= taskContainers.size) {
+ taskContainers.add(taskContainer)
+ } else {
+ taskContainers[index] = taskContainer
+ }
+ }
+ repeat(taskContainers.size - tasks.size) {
+ with(taskContainers.removeLast()) {
+ removeView(thumbnailViewDeprecated)
+ taskThumbnailViewPool.recycle(thumbnailViewDeprecated)
+ }
+ }
+
+ setOrientationState(orientedState)
}
- override fun updateSnapshotRadius() {
- super.updateSnapshotRadius()
- updateFullscreenParams(snapshotDrawParams)
- mTaskContainers.forEach { it.thumbnailView.setFullscreenParams(snapshotDrawParams) }
+ override fun needsUpdate(dataChange: Int, flag: Int) =
+ if (flag == FLAG_UPDATE_THUMBNAIL) super.needsUpdate(dataChange, flag) else false
+
+ // thumbnailView is laid out differently and is handled in onMeasure
+ override fun updateThumbnailSize() {}
+
+ override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
+ if (relativeToDragLayer) {
+ container.dragLayer.getDescendantRectRelativeToSelf(backgroundView, bounds)
+ } else {
+ bounds.set(backgroundView)
+ }
}
- override fun setColorTint(amount: Float, tintColor: Int) {
- mTaskContainers.forEach { it.thumbnailView.dimAlpha = amount }
+ override fun launchTaskAnimated(): RunnableList? {
+ val recentsView = recentsView ?: return null
+ val endCallback = RunnableList()
+ val desktopController = recentsView.desktopRecentsController
+ checkNotNull(desktopController) { "recentsController is null" }
+ desktopController.launchDesktopFromRecents(this) { endCallback.executeAllAndDestroy() }
+ Log.d(TAG, "launchTaskAnimated - launchDesktopFromRecents: ${taskIds.contentToString()}")
+
+ // Callbacks get run from recentsView for case when recents animation already running
+ recentsView.addSideTaskLaunchCallback(endCallback)
+ return endCallback
}
- override fun applyThumbnailSplashAlpha() {
- mTaskContainers.forEach { it.thumbnailView.setSplashAlpha(mTaskThumbnailSplashAlpha) }
- }
-
- public override fun setThumbnailVisibility(visibility: Int, taskId: Int) {
- mTaskContainers.forEach { it.thumbnailView.visibility = visibility }
+ override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
+ launchTasks()
+ callback(true)
}
// Desktop tile can't be in split screen
override fun confirmSecondSplitSelectApp(): Boolean = false
+ // TODO(b/330685808) support overlay for Screenshot action
+ override fun setOverlayEnabled(overlayEnabled: Boolean) {}
+
+ override fun onFullscreenProgressChanged(fullscreenProgress: Float) {
+ // Don't show background while we are transitioning to/from fullscreen
+ backgroundView.visibility = if (fullscreenProgress > 0) INVISIBLE else VISIBLE
+ }
+
+ override fun updateCurrentFullscreenParams() {
+ super.updateCurrentFullscreenParams()
+ updateFullscreenParams(snapshotDrawParams)
+ }
+
+ override fun getThumbnailFullscreenParams() = snapshotDrawParams
+
companion object {
private const val TAG = "DesktopTaskView"
- private const val DEBUG = true
+ private const val DEBUG = false
+ private const val VIEW_POOL_MAX_SIZE = 10
+ // As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
+ private const val VIEW_POOL_INITIAL_SIZE = 0
private val ORIGIN = Point(0, 0)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 4df9414..a8ebe51 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -320,7 +320,7 @@
(FrameLayout.LayoutParams) mBanner.getLayoutParams();
DeviceProfile deviceProfile = mContainer.getDeviceProfile();
layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
- mTaskView.getFirstThumbnailView().getLayoutParams()).bottomMargin;
+ mTaskView.getFirstThumbnailViewDeprecated().getLayoutParams()).bottomMargin;
RecentsPagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
Pair<Float, Float> translations = orientationHandler
.getDwbLayoutTranslations(mTaskView.getMeasuredWidth(),
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index 93a7200..efbfa09 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -18,31 +18,27 @@
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.Context
import android.graphics.PointF
-import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
-import android.view.MotionEvent
import android.view.View
-import android.view.ViewStub
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags
-import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.launcher3.util.TransformingTouchDelegate
-import com.android.quickstep.RecentsModel
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.util.RecentsOrientedState
import com.android.quickstep.util.SplitScreenUtils.Companion.convertLauncherSplitBoundsToShell
import com.android.quickstep.util.SplitSelectStateController
import com.android.systemui.shared.recents.model.Task
-import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition
-import java.util.function.Consumer
/**
* TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
@@ -54,165 +50,191 @@
*
* (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
*/
-class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
+class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
TaskView(context, attrs) {
// TODO(b/336612373): Support new TTV for GroupedTaskView
- private val icon2CenterCoords = FloatArray(2)
- private val digitalWellBeingToast2: DigitalWellBeingToast =
- DigitalWellBeingToast(mContainer, this)
- private lateinit var snapshotView2: TaskThumbnailViewDeprecated
- private lateinit var iconView2: TaskViewIcon
- private lateinit var icon2TouchDelegate: TransformingTouchDelegate
- private var thumbnailLoadRequest2: CancellableTask<ThumbnailData>? = null
- private var iconLoadRequest2: CancellableTask<*>? = null
var splitBoundsConfig: SplitConfigurationOptions.SplitBounds? = null
private set
- @get:Deprecated("Use {@link #mTaskContainers} instead.")
- private val secondTask: Task
- /** Returns the second task bound to this TaskView. */
- get() {
- assert(mTaskContainers.size > 1) { "GroupedTaskView is not bound" }
- return mTaskContainers[1].task
- }
-
@get:PersistentSnapPosition
val snapPosition: Int
/** Returns the [PersistentSnapPosition] of this pair of tasks. */
- get() {
- checkNotNull(splitBoundsConfig) { "mSplitBoundsConfig is null" }
- return splitBoundsConfig!!.snapPosition
- }
+ get() = splitBoundsConfig?.snapPosition ?: STAGE_POSITION_UNDEFINED
- override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
- splitBoundsConfig ?: return super.getThumbnailBounds(bounds, relativeToDragLayer)
- if (relativeToDragLayer) {
- val firstThumbnailBounds = Rect()
- val secondThumbnailBounds = Rect()
- with(mContainer.dragLayer) {
- getDescendantRectRelativeToSelf(mTaskThumbnailViewDeprecated, firstThumbnailBounds)
- getDescendantRectRelativeToSelf(snapshotView2, secondThumbnailBounds)
- }
- bounds.set(firstThumbnailBounds)
- bounds.union(secondThumbnailBounds)
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ val widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ val heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ setMeasuredDimension(widthSize, heightSize)
+ val splitBoundsConfig = splitBoundsConfig ?: return
+ val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
+ if (initSplitTaskId == INVALID_TASK_ID) {
+ pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
+ taskContainers[0].thumbnailViewDeprecated,
+ taskContainers[1].thumbnailViewDeprecated,
+ widthSize,
+ heightSize,
+ splitBoundsConfig,
+ container.deviceProfile,
+ layoutDirection == LAYOUT_DIRECTION_RTL
+ )
+ // Should we be having a separate translation step apart from the measuring above?
+ // The following only applies to large screen for now, but for future reference
+ // we'd want to abstract this out in PagedViewHandlers to get the primary/secondary
+ // translation directions
+ taskContainers[0]
+ .thumbnailViewDeprecated
+ .applySplitSelectTranslateX(taskContainers[0].thumbnailViewDeprecated.translationX)
+ taskContainers[0]
+ .thumbnailViewDeprecated
+ .applySplitSelectTranslateY(taskContainers[0].thumbnailViewDeprecated.translationY)
+ taskContainers[1]
+ .thumbnailViewDeprecated
+ .applySplitSelectTranslateX(taskContainers[1].thumbnailViewDeprecated.translationX)
+ taskContainers[1]
+ .thumbnailViewDeprecated
+ .applySplitSelectTranslateY(taskContainers[1].thumbnailViewDeprecated.translationY)
} else {
- bounds.set(getSnapshotViewBounds(mTaskThumbnailViewDeprecated))
- bounds.union(getSnapshotViewBounds(snapshotView2))
+ // Currently being split with this taskView, let the non-split selected thumbnail
+ // take up full thumbnail area
+ taskContainers
+ .firstOrNull { it.task.key.id != initSplitTaskId }
+ ?.thumbnailViewDeprecated
+ ?.measure(
+ widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(
+ heightSize - container.deviceProfile.overviewTaskThumbnailTopMarginPx,
+ MeasureSpec.EXACTLY
+ )
+ )
+ }
+ if (!enableOverviewIconMenu()) {
+ updateIconPlacement()
}
}
- private fun getSnapshotViewBounds(snapshotView: View): Rect {
- val snapshotViewX = Math.round(snapshotView.x)
- val snapshotViewY = Math.round(snapshotView.y)
- return Rect(
- snapshotViewX,
- snapshotViewY,
- snapshotViewX + snapshotView.width,
- snapshotViewY + snapshotView.height
- )
- }
-
- override fun onFinishInflate() {
- super.onFinishInflate()
- snapshotView2 = findViewById(R.id.bottomright_snapshot)!!
- val iconViewStub2 =
- findViewById<ViewStub>(R.id.bottomRight_icon)!!.apply {
- layoutResource =
- if (enableOverviewIconMenu()) R.layout.icon_app_chip_view
- else R.layout.icon_view
- }
- iconView2 = iconViewStub2.inflate() as TaskViewIcon
- icon2TouchDelegate = TransformingTouchDelegate(iconView2.asView())
+ override fun onRecycle() {
+ super.onRecycle()
+ splitBoundsConfig = null
}
fun bind(
primaryTask: Task,
secondaryTask: Task,
orientedState: RecentsOrientedState,
+ taskOverlayFactory: TaskOverlayFactory,
splitBoundsConfig: SplitConfigurationOptions.SplitBounds?,
) {
cancelPendingLoadTasks()
- setupTaskContainers(primaryTask)
- mTaskContainers =
+ taskContainers =
listOf(
- mTaskContainers[0].apply {
- stagePosition = SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
- },
- TaskContainer(
+ createTaskContainer(
+ primaryTask,
+ R.id.snapshot,
+ R.id.icon,
+ R.id.show_windows,
+ STAGE_POSITION_TOP_OR_LEFT,
+ taskOverlayFactory
+ ),
+ createTaskContainer(
secondaryTask,
- findViewById(R.id.bottomright_snapshot)!!,
- iconView2,
- SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT,
- digitalWellBeingToast2
+ R.id.bottomright_snapshot,
+ R.id.bottomRight_icon,
+ R.id.show_windows_right,
+ STAGE_POSITION_BOTTOM_OR_RIGHT,
+ taskOverlayFactory
)
)
- snapshotView2.bind(secondaryTask)
- this.splitBoundsConfig = splitBoundsConfig
- this.splitBoundsConfig?.let {
- mTaskThumbnailViewDeprecated.previewPositionHelper.setSplitBounds(
- convertLauncherSplitBoundsToShell(it),
- PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT
- )
- snapshotView2.previewPositionHelper.setSplitBounds(
- convertLauncherSplitBoundsToShell(it),
- PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT
- )
- }
+ this.splitBoundsConfig =
+ splitBoundsConfig?.also {
+ taskContainers[0]
+ .thumbnailViewDeprecated
+ .previewPositionHelper
+ .setSplitBounds(
+ convertLauncherSplitBoundsToShell(it),
+ PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT
+ )
+ taskContainers[1]
+ .thumbnailViewDeprecated
+ .previewPositionHelper
+ .setSplitBounds(
+ convertLauncherSplitBoundsToShell(it),
+ PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT
+ )
+ }
setOrientationState(orientedState)
}
- /**
- * Sets up an on-click listener and the visibility for show_windows icon on top of each task.
- */
- override fun setUpShowAllInstancesListener() {
- // sets up the listener for the left/top task
- super.setUpShowAllInstancesListener()
- if (mTaskContainers.size < 2) {
- return
+ override fun setOrientationState(orientationState: RecentsOrientedState) {
+ if (enableOverviewIconMenu()) {
+ splitBoundsConfig?.let {
+ val groupedTaskViewSizes =
+ orientationState.orientationHandler.getGroupedTaskViewSizes(
+ container.deviceProfile,
+ it,
+ layoutParams.width,
+ layoutParams.height
+ )
+ val iconViewMarginStart =
+ resources.getDimensionPixelSize(
+ R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin
+ )
+ val iconViewBackgroundMarginStart =
+ resources.getDimensionPixelSize(
+ R.dimen.task_thumbnail_icon_menu_background_margin_top_start
+ )
+ val iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2
+ // setMaxWidth() needs to be called before mIconView.setIconOrientation which is
+ // called in the super below.
+ (taskContainers[0].iconView as IconAppChipView).setMaxWidth(
+ groupedTaskViewSizes.first.x - iconMargins
+ )
+ (taskContainers[1].iconView as IconAppChipView).setMaxWidth(
+ groupedTaskViewSizes.second.x - iconMargins
+ )
+ }
}
-
- // right/bottom task's base package name
- val taskPackageName = mTaskContainers[1].task.key.packageName
-
- // icon of the right/bottom task
- val showWindowsView = findViewById<View>(R.id.show_windows_right)!!
- updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName))
+ super.setOrientationState(orientationState)
+ updateIconPlacement()
}
- override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
- super.onTaskListVisibilityChanged(visible, changes)
- val model = RecentsModel.INSTANCE[context]
- if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
- if (visible) {
- thumbnailLoadRequest2 =
- model.thumbnailCache.updateThumbnailInBackground(secondTask) {
- snapshotView2.setThumbnail(secondTask, it)
- }
- } else {
- snapshotView2.setThumbnail(null, null)
- // Reset the task thumbnail reference as well (it will be fetched from the cache or
- // reloaded next time we need it)
- secondTask.thumbnail = null
- }
- }
- if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
- if (visible) {
- iconLoadRequest2 =
- model.iconCache.updateIconInBackground(secondTask) {
- setIcon(iconView2, it.icon)
- if (enableOverviewIconMenu()) {
- setText(iconView2, it.title)
- }
- digitalWellBeingToast2.initialize(secondTask)
- digitalWellBeingToast2.setSplitConfiguration(splitBoundsConfig)
- mDigitalWellBeingToast.setSplitConfiguration(splitBoundsConfig)
- }
- } else {
- setIcon(iconView2, null)
- if (enableOverviewIconMenu()) {
- setText(iconView2, null)
- }
- }
+ private fun updateIconPlacement() {
+ val splitBoundsConfig = splitBoundsConfig ?: return
+ val taskIconHeight = container.deviceProfile.overviewTaskIconSizePx
+ val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
+ if (enableOverviewIconMenu()) {
+ val groupedTaskViewSizes =
+ pagedOrientationHandler.getGroupedTaskViewSizes(
+ container.deviceProfile,
+ splitBoundsConfig,
+ layoutParams.width,
+ layoutParams.height
+ )
+ pagedOrientationHandler.setSplitIconParams(
+ taskContainers[0].iconView.asView(),
+ taskContainers[1].iconView.asView(),
+ taskIconHeight,
+ groupedTaskViewSizes.first.x,
+ groupedTaskViewSizes.first.y,
+ layoutParams.height,
+ layoutParams.width,
+ isRtl,
+ container.deviceProfile,
+ splitBoundsConfig
+ )
+ } else {
+ pagedOrientationHandler.setSplitIconParams(
+ taskContainers[0].iconView.asView(),
+ taskContainers[1].iconView.asView(),
+ taskIconHeight,
+ taskContainers[0].thumbnailViewDeprecated.measuredWidth,
+ taskContainers[0].thumbnailViewDeprecated.measuredHeight,
+ measuredHeight,
+ measuredWidth,
+ isRtl,
+ container.deviceProfile,
+ splitBoundsConfig
+ )
}
}
@@ -221,23 +243,8 @@
invalidate()
}
- override fun offerTouchToChildren(event: MotionEvent): Boolean {
- computeAndSetIconTouchDelegate(iconView2, icon2CenterCoords, icon2TouchDelegate)
- return if (icon2TouchDelegate.onTouchEvent(event)) {
- true
- } else super.offerTouchToChildren(event)
- }
-
- override fun cancelPendingLoadTasks() {
- super.cancelPendingLoadTasks()
- thumbnailLoadRequest2?.cancel()
- thumbnailLoadRequest2 = null
- iconLoadRequest2?.cancel()
- iconLoadRequest2 = null
- }
-
override fun launchTaskAnimated(): RunnableList? {
- if (mTaskContainers.isEmpty()) {
+ if (taskContainers.isEmpty()) {
Log.d(TAG, "launchTaskAnimated - task is not bound")
return null
}
@@ -259,8 +266,8 @@
return endCallback
}
- override fun launchTask(callback: Consumer<Boolean>, isQuickswitch: Boolean) {
- launchTaskInternal(isQuickswitch, false, callback /*launchingExistingTaskview*/)
+ override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
+ launchTaskInternal(isQuickSwitch, false, callback /*launchingExistingTaskview*/)
}
/**
@@ -272,15 +279,14 @@
private fun launchTaskInternal(
isQuickSwitch: Boolean,
launchingExistingTaskView: Boolean,
- callback: Consumer<Boolean>
+ callback: (launched: Boolean) -> Unit
) {
- check(mTaskContainers.size >= 2) { "task not bound" }
recentsView?.let {
it.splitSelectController.launchExistingSplitPair(
if (launchingExistingTaskView) this else null,
- mTaskContainers[0].task.key.id,
- mTaskContainers[1].task.key.id,
- SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
+ taskContainers[0].task.key.id,
+ taskContainers[1].task.key.id,
+ STAGE_POSITION_TOP_OR_LEFT,
callback,
isQuickSwitch,
snapPosition
@@ -289,12 +295,6 @@
}
}
- public override fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
- super.refreshThumbnails(thumbnailDatas)
- thumbnailDatas?.get(secondTask.key.id)?.let { snapshotView2.setThumbnail(secondTask, it) }
- ?: { snapshotView2.refresh() }
- }
-
/**
* Returns taskId that split selection was initiated with, [INVALID_TASK_ID] if no tasks in this
* TaskView are part of split selection
@@ -312,14 +312,14 @@
// checks below aren't reliable since both of those views may be gone/transformed
val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
if (initSplitTaskId != INVALID_TASK_ID) {
- return if (initSplitTaskId == firstTask!!.key.id) 1 else 0
+ return if (initSplitTaskId == taskContainers[0].task.key.id) 1 else 0
}
}
// Check which of the two apps was selected
if (
- iconView2.asView().containsPoint(mLastTouchDownPosition) ||
- snapshotView2.containsPoint(mLastTouchDownPosition)
+ taskContainers[1].iconView.asView().containsPoint(lastTouchDownPosition) ||
+ taskContainers[1].thumbnailViewDeprecated.containsPoint(lastTouchDownPosition)
) {
return 1
}
@@ -332,60 +332,6 @@
return Utilities.pointInView(this, localPos[0], localPos[1], 0f /* slop */)
}
- override fun onRecycle() {
- super.onRecycle()
- snapshotView2.setThumbnail(secondTask, null)
- splitBoundsConfig = null
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val widthSize = MeasureSpec.getSize(widthMeasureSpec)
- val heightSize = MeasureSpec.getSize(heightMeasureSpec)
- setMeasuredDimension(widthSize, heightSize)
- val splitBoundsConfig = splitBoundsConfig ?: return
- val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
- if (initSplitTaskId == INVALID_TASK_ID) {
- pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
- mTaskThumbnailViewDeprecated,
- snapshotView2,
- widthSize,
- heightSize,
- splitBoundsConfig,
- mContainer.deviceProfile,
- layoutDirection == LAYOUT_DIRECTION_RTL
- )
- // Should we be having a separate translation step apart from the measuring above?
- // The following only applies to large screen for now, but for future reference
- // we'd want to abstract this out in PagedViewHandlers to get the primary/secondary
- // translation directions
- mTaskThumbnailViewDeprecated.applySplitSelectTranslateX(
- mTaskThumbnailViewDeprecated.translationX
- )
- mTaskThumbnailViewDeprecated.applySplitSelectTranslateY(
- mTaskThumbnailViewDeprecated.translationY
- )
- snapshotView2.applySplitSelectTranslateX(snapshotView2.translationX)
- snapshotView2.applySplitSelectTranslateY(snapshotView2.translationY)
- } else {
- // Currently being split with this taskView, let the non-split selected thumbnail
- // take up full thumbnail area
- mTaskContainers
- .firstOrNull { it.task.key.id != initSplitTaskId }
- ?.thumbnailView
- ?.measure(
- widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(
- heightSize - mContainer.deviceProfile.overviewTaskThumbnailTopMarginPx,
- MeasureSpec.EXACTLY
- )
- )
- }
- if (!enableOverviewIconMenu()) {
- updateIconPlacement()
- }
- }
-
override fun setOverlayEnabled(overlayEnabled: Boolean) {
if (FeatureFlags.enableAppPairs()) {
super.setOverlayEnabled(overlayEnabled)
@@ -394,135 +340,6 @@
}
}
- override fun setOrientationState(orientationState: RecentsOrientedState) {
- if (enableOverviewIconMenu()) {
- splitBoundsConfig?.let {
- val groupedTaskViewSizes =
- orientationState.orientationHandler.getGroupedTaskViewSizes(
- mContainer.deviceProfile,
- it,
- layoutParams.width,
- layoutParams.height
- )
- val iconViewMarginStart =
- resources.getDimensionPixelSize(
- R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin
- )
- val iconViewBackgroundMarginStart =
- resources.getDimensionPixelSize(
- R.dimen.task_thumbnail_icon_menu_background_margin_top_start
- )
- val iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2
- // setMaxWidth() needs to be called before mIconView.setIconOrientation which is
- // called in the super below.
- (mIconView as IconAppChipView).setMaxWidth(
- groupedTaskViewSizes.first.x - iconMargins
- )
- (iconView2 as IconAppChipView).setMaxWidth(
- groupedTaskViewSizes.second.x - iconMargins
- )
- }
- }
- super.setOrientationState(orientationState)
- iconView2.setIconOrientation(orientationState, isGridTask())
- updateIconPlacement()
- }
-
- override fun setThumbnailOrientation(orientationState: RecentsOrientedState?) {
- super.setThumbnailOrientation(orientationState)
- digitalWellBeingToast2.initialize(secondTask)
- }
-
- private fun updateIconPlacement() {
- val splitBoundsConfig = splitBoundsConfig ?: return
- val taskIconHeight = mContainer.deviceProfile.overviewTaskIconSizePx
- val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
- if (enableOverviewIconMenu()) {
- val groupedTaskViewSizes =
- pagedOrientationHandler.getGroupedTaskViewSizes(
- mContainer.deviceProfile,
- splitBoundsConfig,
- layoutParams.width,
- layoutParams.height
- )
- pagedOrientationHandler.setSplitIconParams(
- mIconView.asView(),
- iconView2.asView(),
- taskIconHeight,
- groupedTaskViewSizes.first.x,
- groupedTaskViewSizes.first.y,
- layoutParams.height,
- layoutParams.width,
- isRtl,
- mContainer.deviceProfile,
- splitBoundsConfig
- )
- } else {
- pagedOrientationHandler.setSplitIconParams(
- mIconView.asView(),
- iconView2.asView(),
- taskIconHeight,
- mTaskThumbnailViewDeprecated.measuredWidth,
- mTaskThumbnailViewDeprecated.measuredHeight,
- measuredHeight,
- measuredWidth,
- isRtl,
- mContainer.deviceProfile,
- splitBoundsConfig
- )
- }
- }
-
- override fun updateSnapshotRadius() {
- super.updateSnapshotRadius()
- snapshotView2.setFullscreenParams(mCurrentFullscreenParams)
- }
-
- override fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
- super.setIconsAndBannersTransitionProgress(progress, invert)
- // Value set by super call
- val scale = mIconView.alpha
- iconView2.setContentAlpha(scale)
- digitalWellBeingToast2.updateBannerOffset(1f - scale)
- }
-
- override fun setColorTint(amount: Float, tintColor: Int) {
- super.setColorTint(amount, tintColor)
- iconView2.setIconColorTint(tintColor, amount)
- snapshotView2.dimAlpha = amount
- digitalWellBeingToast2.setBannerColorTint(tintColor, amount)
- }
-
- override fun applyThumbnailSplashAlpha() {
- super.applyThumbnailSplashAlpha()
- snapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha)
- }
-
- override fun refreshTaskThumbnailSplash() {
- super.refreshTaskThumbnailSplash()
- snapshotView2.refreshSplashView()
- }
-
- override fun resetViewTransforms() {
- super.resetViewTransforms()
- snapshotView2.resetViewTransforms()
- }
-
- /**
- * Sets visibility for thumbnails and associated elements (DWB banners). IconView is unaffected.
- *
- * When setting INVISIBLE, sets the visibility for the last selected child task. When setting
- * VISIBLE (as a reset), sets the visibility for both tasks.
- */
- public override fun setThumbnailVisibility(visibility: Int, taskId: Int) {
- mTaskContainers.forEach {
- if (visibility == VISIBLE || it.task.key.id == taskId) {
- it.thumbnailView.visibility = visibility
- it.digitalWellBeingToast.setBannerVisibility(visibility)
- }
- }
- }
-
companion object {
private const val TAG = "GroupedTaskView"
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 8d1907f..5284b44 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -56,6 +56,8 @@
import com.android.quickstep.util.SplitSelectStateController;
import com.android.systemui.shared.recents.model.Task;
+import kotlin.Unit;
+
/**
* {@link RecentsView} used in Launcher activity
*/
@@ -107,7 +109,7 @@
}
@Override
- protected void onTaskLaunchAnimationEnd(boolean success) {
+ protected Unit onTaskLaunchAnimationEnd(boolean success) {
if (success) {
getStateManager().moveToRestState();
} else {
@@ -115,6 +117,7 @@
mContainer.getAllAppsController().setState(state);
}
super.onTaskLaunchAnimationEnd(success);
+ return Unit.INSTANCE;
}
@Override
@@ -143,7 +146,7 @@
@Override
public void onStateTransitionStart(LauncherState toState) {
- setOverviewStateEnabled(toState.overviewUi);
+ setOverviewStateEnabled(toState.isRecentsViewVisible);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
@@ -154,7 +157,7 @@
}
// Set border after select mode changes to avoid showing border during state transition
- if (!toState.overviewUi || toState == OVERVIEW_MODAL_TASK) {
+ if (!toState.isRecentsViewVisible || toState == OVERVIEW_MODAL_TASK) {
setTaskBorderEnabled(false);
}
@@ -178,7 +181,7 @@
setOverviewSelectEnabled(false);
}
- if (finalState.overviewUi && finalState != OVERVIEW_MODAL_TASK) {
+ if (finalState.isRecentsViewVisible && finalState != OVERVIEW_MODAL_TASK) {
setTaskBorderEnabled(true);
}
@@ -203,7 +206,7 @@
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// Do not let touch escape to siblings below this view.
- return result || getStateManager().getState().overviewUi;
+ return result || getStateManager().getState().isRecentsViewVisible;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 39448e9..4e5d646 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -45,6 +45,7 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ORIENTATION_CHANGED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -145,6 +146,7 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
@@ -168,7 +170,6 @@
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.BaseContainerInterface;
-import com.android.quickstep.DesktopModeStatus;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.RecentsAnimationController;
@@ -216,6 +217,9 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.common.pip.IPipAnimationListener;
+import com.android.wm.shell.shared.DesktopModeStatus;
+
+import kotlin.Unit;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1002,7 +1006,8 @@
if (container == null || taskId != container.getTask().key.id) {
continue;
}
- container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);
+ container.getThumbnailViewDeprecated().setThumbnail(container.getTask(),
+ thumbnailData);
}
}
}
@@ -1014,11 +1019,10 @@
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = requireTaskViewAt(i);
Task task = tv.getFirstTask();
- if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
- && task.key.userId == user.getIdentifier()) {
+ if (pkg.equals(task.key.getPackageName()) && task.key.userId == user.getIdentifier()) {
task.icon = null;
- TaskViewIcon firstIconView = tv.getFirstIconView();
- if (firstIconView != null && firstIconView.getDrawable() != null) {
+ if (tv.getTaskContainers().stream().anyMatch(
+ container -> container.getIconView().getDrawable() != null)) {
tv.onTaskListVisibilityChanged(true /* visible */);
}
}
@@ -1055,7 +1059,7 @@
}
Task task = taskAttributes.getTask();
TaskThumbnailViewDeprecated taskThumbnailViewDeprecated =
- taskAttributes.getThumbnailView();
+ taskAttributes.getThumbnailViewDeprecated();
taskThumbnailViewDeprecated.setThumbnail(task, thumbnail, refreshNow);
// thumbnailData can contain 1-2 ids, but they should correspond to the same
// TaskView, so overwriting is ok
@@ -1738,7 +1742,7 @@
int[] currentTaskIds;
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
- if (currentTaskView != null && currentTaskView.getFirstTask() != null) {
+ if (currentTaskView != null) {
currentTaskIds = currentTaskView.getTaskIds();
} else {
currentTaskIds = new int[0];
@@ -1768,12 +1772,12 @@
// If we are entering Overview as a result of initiating a split from somewhere else
// (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
- int stagedTaskIdToBeRemovedFromGrid;
+ int stagedTaskIdToBeRemoved;
if (isSplitSelectionActive()) {
- stagedTaskIdToBeRemovedFromGrid = mSplitSelectStateController.getInitialTaskId();
+ stagedTaskIdToBeRemoved = mSplitSelectStateController.getInitialTaskId();
updateCurrentTaskActionsVisibility();
} else {
- stagedTaskIdToBeRemovedFromGrid = INVALID_TASK_ID;
+ stagedTaskIdToBeRemoved = INVALID_TASK_ID;
}
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
@@ -1785,46 +1789,36 @@
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean isRemovalNeeded = stagedTaskIdToBeRemovedFromGrid != INVALID_TASK_ID
- && groupTask.containsTask(stagedTaskIdToBeRemovedFromGrid);
+ boolean isRemovalNeeded = stagedTaskIdToBeRemoved != INVALID_TASK_ID
+ && groupTask.containsTask(stagedTaskIdToBeRemoved);
- TaskView taskView;
- if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
- // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
- // to be a temporary container for the remaining task.
- taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
- } else {
- taskView = getTaskViewFromPool(groupTask.taskViewType);
+ if (isRemovalNeeded && !groupTask.hasMultipleTasks()) {
+ // If the task we need to remove is not part of a pair, avoiding creating the
+ // TaskView.
+ continue;
}
- addView(taskView);
-
- if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
- if (groupTask.task1.key.id == stagedTaskIdToBeRemovedFromGrid) {
- taskView.bind(groupTask.task2, mOrientationState);
- } else {
- taskView.bind(groupTask.task1, mOrientationState);
- }
- } else if (isRemovalNeeded) {
- // If the task we need to remove is not part of a pair, bind it to the TaskView
- // first (to prevent problems), then remove the whole thing.
- taskView.bind(groupTask.task1, mOrientationState);
- removeView(taskView);
- } else if (taskView instanceof GroupedTaskView) {
+ // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
+ // to be a temporary container for the remaining task.
+ TaskView taskView = getTaskViewFromPool(
+ isRemovalNeeded ? TaskView.Type.SINGLE : groupTask.taskViewType);
+ if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
-
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
- groupTask.mSplitBounds);
+ mTaskOverlayFactory, groupTask.mSplitBounds);
} else if (taskView instanceof DesktopTaskView) {
((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
- mOrientationState);
+ mOrientationState, mTaskOverlayFactory);
mDesktopTaskView = (DesktopTaskView) taskView;
} else {
- taskView.bind(groupTask.task1, mOrientationState);
+ Task task = groupTask.task1.key.id == stagedTaskIdToBeRemoved ? groupTask.task2
+ : groupTask.task1;
+ taskView.bind(task, mOrientationState, mTaskOverlayFactory);
}
+ addView(taskView);
// enables instance filtering if the feature flag for it is on
if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
@@ -2085,12 +2079,17 @@
: View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(getPagedOrientationHandler().getDegreesRotated());
- if (forceRecreateDragLayerControllers
- || !getPagedOrientationHandler().equals(oldOrientationHandler)) {
+ boolean isOrientationHandlerChanged =
+ !getPagedOrientationHandler().equals(oldOrientationHandler);
+ if (forceRecreateDragLayerControllers || isOrientationHandlerChanged) {
// Changed orientations, update controllers so they intercept accordingly.
mContainer.getDragLayer().recreateControllers();
onOrientationChanged();
resetTaskVisuals();
+ // Log fake orientation changed.
+ if (isOrientationHandlerChanged) {
+ logOrientationChanged();
+ }
}
boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
@@ -2175,7 +2174,8 @@
}
for (int i = 0; i < taskCount; i++) {
TaskView taskView = requireTaskViewAt(i);
- taskView.updateTaskSize();
+ taskView.updateTaskSize(mLastComputedTaskSize, mLastComputedGridTaskSize,
+ mLastComputedCarouselTaskSize);
taskView.setNonGridTranslationX(accumulatedTranslationX);
taskView.setNonGridPivotTranslationX(translateXToMiddle);
// Compensate space caused by TaskView scaling.
@@ -2794,26 +2794,24 @@
if (needDesktopTask) {
taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
- addView(taskView, 0);
((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
- mOrientationState);
+ mOrientationState, mTaskOverlayFactory);
} else if (needGroupTaskView) {
taskView = getTaskViewFromPool(TaskView.Type.GROUPED);
mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
- addView(taskView, 0);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
// the actual app running we won't need to show the thumbnail until all the tasks
// load later anyways
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
- mOrientationState, mSplitBoundsConfig);
+ mOrientationState, mTaskOverlayFactory, mSplitBoundsConfig);
} else {
taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
- addView(taskView, 0);
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
mTmpRunningTasks = new Task[]{runningTasks[0]};
- taskView.bind(mTmpRunningTasks[0], mOrientationState);
+ taskView.bind(mTmpRunningTasks[0], mOrientationState, mTaskOverlayFactory);
}
+ addView(taskView, 0);
runningTaskViewId = taskView.getTaskViewId();
if (wasEmpty) {
addView(mClearAllButton);
@@ -2911,7 +2909,7 @@
mRunningTaskShowScreenshot = showScreenshot;
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
- runningTaskView.setShowScreenshot(mRunningTaskShowScreenshot);
+ runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot);
}
}
@@ -3755,7 +3753,7 @@
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
- anim.setFloat(taskView, TaskView.SNAPSHOT_SCALE, scale,
+ anim.setFloat(taskView, TaskView.DISMISS_SCALE, scale,
clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
@@ -3826,19 +3824,17 @@
if (success) {
if (shouldRemoveTask) {
- if (dismissedTaskView.getFirstTask() != null) {
- if (dismissedTaskView.isRunningTask()) {
- finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
- () -> removeTaskInternal(dismissedTaskViewId));
- } else {
- removeTaskInternal(dismissedTaskViewId);
- }
- announceForAccessibility(
- getResources().getString(R.string.task_view_closed));
- mContainer.getStatsLogManager().logger()
- .withItemInfo(dismissedTaskView.getItemInfo())
- .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
+ if (dismissedTaskView.isRunningTask()) {
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ () -> removeTaskInternal(dismissedTaskViewId));
+ } else {
+ removeTaskInternal(dismissedTaskViewId);
}
+ announceForAccessibility(
+ getResources().getString(R.string.task_view_closed));
+ mContainer.getStatsLogManager().logger()
+ .withItemInfo(dismissedTaskView.getFirstItemInfo())
+ .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
}
int pageToSnapTo = mCurrentPage;
@@ -4356,7 +4352,10 @@
public void updateRecentsRotation() {
final int rotation = TraceHelper.allowIpcs(
"RecentsView.updateRecentsRotation", () -> mContainer.getDisplay().getRotation());
- mOrientationState.setRecentsRotation(rotation);
+ // Log real orientation change.
+ if (mOrientationState.setRecentsRotation(rotation)) {
+ logOrientationChanged();
+ }
}
public void setLayoutRotation(int touchRotation, int displayRotation) {
@@ -4745,7 +4744,8 @@
*/
public void resetModalVisuals() {
if (mSelectedTask != null) {
- mSelectedTask.getFirstThumbnailView().getTaskOverlay().resetModalVisuals();
+ mSelectedTask.taskContainers.forEach(
+ taskContainer -> taskContainer.getOverlay().resetModalVisuals());
}
}
@@ -4763,8 +4763,8 @@
public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent) {
mSplitHiddenTaskView = taskView;
- mSplitSelectStateController.setInitialTaskSelect(null /*intent*/,
- stagePosition, taskView.getItemInfo(), splitEvent, taskView.getFirstTask().key.id);
+ mSplitSelectStateController.setInitialTaskSelect(null /*intent*/, stagePosition,
+ taskView.getFirstItemInfo(), splitEvent, taskView.getFirstTask().key.id);
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
@@ -4817,7 +4817,7 @@
== mSplitSelectStateController.getInitialTaskId();
TaskContainer taskContainer = mSplitHiddenTaskView
.getTaskContainers().get(primaryTaskSelected ? 1 : 0);
- TaskThumbnailViewDeprecated thumbnail = taskContainer.getThumbnailView();
+ TaskThumbnailViewDeprecated thumbnail = taskContainer.getThumbnailViewDeprecated();
mSplitSelectStateController.getSplitAnimationController()
.addInitialSplitFromPair(taskContainer, builder,
mContainer.getDeviceProfile(),
@@ -5217,7 +5217,7 @@
updateGridProperties();
updateScrollSynchronously();
- int targetSysUiFlags = tv.getFirstThumbnailView().getSysUiStatusNavFlags();
+ int targetSysUiFlags = tv.getFirstThumbnailViewDeprecated().getSysUiStatusNavFlags();
final boolean[] passedOverviewThreshold = new boolean[] {false};
ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
progressAnim.addUpdateListener(animator -> {
@@ -5281,11 +5281,8 @@
} else {
tv.launchTask(this::onTaskLaunchAnimationEnd);
}
- Task task = tv.getFirstTask();
- if (task != null) {
- mContainer.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
- .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
- }
+ mContainer.getStatsLogManager().logger().withItemInfo(tv.getFirstItemInfo())
+ .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
} else {
onTaskLaunchAnimationEnd(false);
}
@@ -5294,10 +5291,11 @@
return mPendingAnimation;
}
- protected void onTaskLaunchAnimationEnd(boolean success) {
+ protected Unit onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
}
+ return Unit.INSTANCE;
}
@Override
@@ -5913,7 +5911,7 @@
return;
}
- taskView.setShowScreenshot(true);
+ taskView.setShouldShowScreenshot(true);
for (TaskContainer container : taskView.getTaskContainers()) {
if (container == null) {
continue;
@@ -5921,7 +5919,7 @@
ThumbnailData td =
mRecentsAnimationController.screenshotTask(container.getTask().key.id);
- TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailView();
+ TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailViewDeprecated();
if (td != null) {
thumbnailView.setThumbnail(container.getTask(), td);
} else {
@@ -5942,7 +5940,7 @@
Runnable onFinishRunnable) {
final TaskView taskView = getRunningTaskView();
if (taskView != null) {
- taskView.setShowScreenshot(true);
+ taskView.setShouldShowScreenshot(true);
taskView.refreshThumbnails(thumbnailDatas);
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
} else {
@@ -6302,6 +6300,24 @@
successCallback.run();
}
+ // Logs when the orientation of Overview changes. We log both real and fake orientation changes.
+ private void logOrientationChanged() {
+ // Only log when Overview is showing.
+ if (mOverviewStateEnabled) {
+ mContainer.getStatsLogManager()
+ .logger()
+ .withContainerInfo(
+ LauncherAtom.ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(
+ LauncherAtom.TaskSwitcherContainer.newBuilder()
+ .setOrientationHandler(
+ getPagedOrientationHandler()
+ .getHandlerTypeForLogging()))
+ .build())
+ .log(LAUNCHER_OVERVIEW_ORIENTATION_CHANGED);
+ }
+ }
+
public interface TaskLaunchListener {
void onTaskLaunched();
}
diff --git a/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
similarity index 80%
rename from src/com/android/quickstep/views/RecentsViewContainer.java
rename to quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index 0c3f4f1..060c71e 100644
--- a/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -27,6 +27,7 @@
import android.view.Window;
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
@@ -165,4 +166,36 @@
* Begins transition from overview back to homescreen
*/
void returnToHomescreen();
+
+ /**
+ * True if the overview panel is visible.
+ * @return Boolean
+ */
+ boolean isRecentsViewVisible();
+
+ /**
+ * Overwrites any logged item in Launcher that doesn't have a container with the
+ * {@link com.android.launcher3.touch.PagedOrientationHandler} in use for Overview.
+ *
+ * @param itemInfoBuilder {@link LauncherAtom.ItemInfo.Builder}
+ */
+ default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) {
+ if (!itemInfoBuilder.getContainerInfo().hasTaskSwitcherContainer()) {
+ return;
+ }
+
+ if (!isRecentsViewVisible()) {
+ return;
+ }
+
+ RecentsView<?, ?> recentsView = getOverviewPanel();
+ var orientationForLogging =
+ recentsView.getPagedOrientationHandler().getHandlerTypeForLogging();
+ itemInfoBuilder.setContainerInfo(
+ LauncherAtom.ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(
+ LauncherAtom.TaskSwitcherContainer.newBuilder()
+ .setOrientationHandler(orientationForLogging))
+ .build());
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 578d471..59ffc39 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -241,12 +241,13 @@
mContainer.getDragLayer().getDescendantRectRelativeToSelf(
enableOverviewIconMenu()
? getIconView().findViewById(R.id.icon_view_menu_anchor)
- : taskContainer.getThumbnailView(),
+ : taskContainer.getThumbnailViewDeprecated(),
sTempRect);
Rect insets = mContainer.getDragLayer().getInsets();
BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
- params.width = orientationHandler.getTaskMenuWidth(taskContainer.getThumbnailView(),
- deviceProfile, taskContainer.getStagePosition());
+ params.width = orientationHandler.getTaskMenuWidth(
+ taskContainer.getThumbnailViewDeprecated(), deviceProfile,
+ taskContainer.getStagePosition());
// Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
params.gravity = Gravity.LEFT;
setLayoutParams(params);
@@ -279,10 +280,10 @@
// Margin that insets the menuView inside the taskView
float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
- mTaskContainer.getThumbnailView(), deviceProfile, taskInsetMargin,
+ mTaskContainer.getThumbnailViewDeprecated(), deviceProfile, taskInsetMargin,
getIconView()));
setTranslationY(orientationHandler.getTaskMenuY(
- thumbnailAlignedY, mTaskContainer.getThumbnailView(),
+ thumbnailAlignedY, mTaskContainer.getThumbnailViewDeprecated(),
mTaskContainer.getStagePosition(), this, taskInsetMargin,
getIconView()));
}
@@ -367,7 +368,7 @@
}
mOpenCloseAnimator.playTogether(mRevealAnimator,
ObjectAnimator.ofFloat(
- mTaskContainer.getThumbnailView(), DIM_ALPHA,
+ mTaskContainer.getThumbnailViewDeprecated(), DIM_ALPHA,
closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 7adc32e..659cc0c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -43,10 +43,7 @@
companion object {
const val TAG = "TaskMenuViewWithArrow"
- fun <T> showForTask(
- taskContainer: TaskContainer,
- alignedOptionIndex: Int = 0
- ): Boolean where T : RecentsViewContainer, T : Context {
+ fun showForTask(taskContainer: TaskContainer, alignedOptionIndex: Int = 0): Boolean {
val container: RecentsViewContainer =
RecentsViewContainer.containerFromContext(taskContainer.taskView.context)
val taskMenuViewWithArrow =
@@ -54,7 +51,7 @@
R.layout.task_menu_with_arrow,
container.dragLayer,
false
- ) as TaskMenuViewWithArrow<T>
+ ) as TaskMenuViewWithArrow<*>
return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignedOptionIndex)
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
index 21c6ca8..447002f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewDeprecated.java
@@ -128,8 +128,7 @@
};
private final RecentsViewContainer mContainer;
- @Nullable
- private TaskOverlay mOverlay;
+ private TaskOverlay<?> mOverlay;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mSplashBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -188,8 +187,9 @@
/**
* Updates the thumbnail to draw the provided task
*/
- public void bind(Task task) {
- getTaskOverlay().reset();
+ public void bind(Task task, TaskOverlay<?> overlay) {
+ mOverlay = overlay;
+ mOverlay.reset();
mTask = task;
int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
mPaint.setColor(color);
@@ -199,6 +199,17 @@
}
/**
+ * Sets TaskOverlay without binding a task.
+ *
+ * @deprecated Should only be used when using new
+ * {@link com.android.quickstep.task.thumbnail.TaskThumbnailView}.
+ */
+ @Deprecated
+ public void setTaskOverlay(TaskOverlay<?> overlay) {
+ mOverlay = overlay;
+ }
+
+ /**
* Updates the thumbnail.
*
* @param refreshNow whether the {@code thumbnailData} will be used to redraw immediately.
@@ -212,8 +223,8 @@
boolean refreshNow) {
mTask = task;
boolean thumbnailWasNull = mThumbnailData == null;
- mThumbnailData =
- (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
+ mThumbnailData = (thumbnailData != null && thumbnailData.getThumbnail() != null)
+ ? thumbnailData : null;
if (mTask != null) {
updateSplashView(mTask.icon);
}
@@ -238,8 +249,8 @@
* @param shouldRefreshOverlay whether to re-initialize overlay
*/
private void refresh(boolean shouldRefreshOverlay) {
- if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
- Bitmap bm = mThumbnailData.thumbnail;
+ if (mThumbnailData != null && mThumbnailData.getThumbnail() != null) {
+ Bitmap bm = mThumbnailData.getThumbnail();
bm.prepareToDraw();
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(mBitmapShader);
@@ -251,7 +262,7 @@
mBitmapShader = null;
mThumbnailData = null;
mPaint.setShader(null);
- getTaskOverlay().reset();
+ mOverlay.reset();
}
updateThumbnailPaintFilter();
}
@@ -279,13 +290,6 @@
invalidate();
}
- public TaskOverlay getTaskOverlay() {
- if (mOverlay == null) {
- mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this);
- }
- return mOverlay;
- }
-
public float getDimAlpha() {
return mDimAlpha;
}
@@ -303,8 +307,10 @@
}
RectF bitmapRect = new RectF(
- 0, 0,
- mThumbnailData.thumbnail.getWidth(), mThumbnailData.thumbnail.getHeight());
+ 0,
+ 0,
+ mThumbnailData.getThumbnail().getWidth(),
+ mThumbnailData.getThumbnail().getHeight());
RectF viewRect = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());
// The position helper matrix tells us how to transform the bitmap to fit the view, the
@@ -348,7 +354,7 @@
canvas.save();
// Draw the insets if we're being drawn fullscreen (we do this for quick switch).
drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(),
- mFullscreenParams.mCurrentDrawnCornerRadius);
+ mFullscreenParams.getCurrentDrawnCornerRadius());
canvas.restore();
}
@@ -358,13 +364,13 @@
public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
mFullscreenParams = fullscreenParams;
- getTaskOverlay().setFullscreenParams(fullscreenParams);
invalidate();
}
public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
float cornerRadius) {
- if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
+ if (mTask != null && getTaskView().isRunningTask()
+ && !getTaskView().getShouldShowScreenshot()) {
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
mDimmingPaintAfterClearing);
@@ -502,13 +508,13 @@
}
private boolean isThumbnailAspectRatioDifferentFromThumbnailData() {
- if (mThumbnailData == null || mThumbnailData.thumbnail == null) {
+ if (mThumbnailData == null || mThumbnailData.getThumbnail() == null) {
return false;
}
float thumbnailViewAspect = getWidth() / (float) getHeight();
- float thumbnailDataAspect =
- mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight();
+ float thumbnailDataAspect = mThumbnailData.getThumbnail().getWidth()
+ / (float) mThumbnailData.getThumbnail().getHeight();
return isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
@@ -534,10 +540,10 @@
*/
private void refreshOverlay() {
if (mOverlayEnabled) {
- getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(),
+ mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(),
mPreviewPositionHelper.isOrientationChanged());
} else {
- getTaskOverlay().reset();
+ mOverlay.reset();
}
}
@@ -559,10 +565,9 @@
DeviceProfile dp = mContainer.getDeviceProfile();
mPreviewPositionHelper.setOrientationChanged(false);
if (mBitmapShader != null && mThumbnailData != null) {
- mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
- mThumbnailData.thumbnail.getHeight());
- int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
- .getRecentsActivityRotation();
+ mPreviewRect.set(0, 0, mThumbnailData.getThumbnail().getWidth(),
+ mThumbnailData.getThumbnail().getHeight());
+ int currentRotation = getTaskView().getOrientedState().getRecentsActivityRotation();
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
getMeasuredWidth(), getMeasuredHeight(), dp.isTablet, currentRotation, isRtl);
@@ -594,7 +599,7 @@
if (mThumbnailData == null) {
return null;
}
- return mThumbnailData.thumbnail;
+ return mThumbnailData.getThumbnail();
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
deleted file mode 100644
index cc24bc3..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ /dev/null
@@ -1,2060 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.view.Display.DEFAULT_DISPLAY;
-import static android.widget.Toast.LENGTH_SHORT;
-
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.Flags.enableCursorHoverStates;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
-import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
-import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
-import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.annotation.IdRes;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
-import android.view.TouchDelegate;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.Toast;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.app.animation.Interpolators;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.CancellableTask;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.TransformingTouchDelegate;
-import com.android.launcher3.util.ViewPool.Reusable;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RemoteAnimationTargets;
-import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.TaskIconCache;
-import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TaskViewUtils;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.task.thumbnail.TaskThumbnail;
-import com.android.quickstep.task.thumbnail.TaskThumbnailView;
-import com.android.quickstep.task.viewmodel.TaskViewData;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.BorderAnimator;
-import com.android.quickstep.util.RecentsOrientedState;
-import com.android.quickstep.util.SplitSelectStateController;
-import com.android.quickstep.util.TaskCornerRadius;
-import com.android.quickstep.util.TaskRemovedDuringLaunchListener;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import kotlin.Unit;
-
-import java.lang.annotation.Retention;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
-
-
-/**
- * A task in the Recents view.
- */
-public class TaskView extends FrameLayout implements Reusable {
-
- private static final String TAG = "TaskView";
- public static final int FLAG_UPDATE_ICON = 1;
- public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
- public static final int FLAG_UPDATE_CORNER_RADIUS = FLAG_UPDATE_THUMBNAIL << 1;
-
- public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL
- | FLAG_UPDATE_CORNER_RADIUS;
-
- /**
- * Used in conjunction with {@link #onTaskListVisibilityChanged(boolean, int)}, providing more
- * granularity on which components of this task require an update
- */
- @Retention(SOURCE)
- @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS})
- public @interface TaskDataChanges {
- }
-
- /**
- * Type of task view
- */
- @Retention(SOURCE)
- @IntDef({Type.SINGLE, Type.GROUPED, Type.DESKTOP})
- public @interface Type {
- int SINGLE = 1;
- int GROUPED = 2;
- int DESKTOP = 3;
- }
-
- /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
- public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
-
- private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
- private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
-
- public static final long SCALE_ICON_DURATION = 120;
- private static final long DIM_ANIM_DURATION = 700;
-
- /**
- * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
- * setting the touch bounds at construction, so we'd repeatedly be created many instances
- * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch
- * delegated bounds only to be updated.
- */
- private TransformingTouchDelegate mIconTouchDelegate;
-
- private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
- Collections.singletonList(new Rect());
-
- public static final FloatProperty<TaskView> FOCUS_TRANSITION =
- new FloatProperty<>("focusTransition") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setIconsAndBannersTransitionProgress(v, false /* invert */);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mFocusTransitionProgress;
- }
- };
-
- private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
- new FloatProperty<>("splitSelectTranslationX") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setSplitSelectTranslationX(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mSplitSelectTranslationX;
- }
- };
-
- private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
- new FloatProperty<>("splitSelectTranslationY") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setSplitSelectTranslationY(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mSplitSelectTranslationY;
- }
- };
-
- private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
- new FloatProperty<>("dismissTranslationX") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setDismissTranslationX(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mDismissTranslationX;
- }
- };
-
- private static final FloatProperty<TaskView> DISMISS_TRANSLATION_Y =
- new FloatProperty<>("dismissTranslationY") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setDismissTranslationY(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mDismissTranslationY;
- }
- };
-
- private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
- new FloatProperty<>("taskOffsetTranslationX") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setTaskOffsetTranslationX(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mTaskOffsetTranslationX;
- }
- };
-
- private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
- new FloatProperty<>("taskOffsetTranslationY") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setTaskOffsetTranslationY(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mTaskOffsetTranslationY;
- }
- };
-
- private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_X =
- new FloatProperty<>("taskResistanceTranslationX") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setTaskResistanceTranslationX(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mTaskResistanceTranslationX;
- }
- };
-
- private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_Y =
- new FloatProperty<>("taskResistanceTranslationY") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setTaskResistanceTranslationY(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mTaskResistanceTranslationY;
- }
- };
-
- public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X =
- new FloatProperty<>("gridEndTranslationX") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setGridEndTranslationX(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mGridEndTranslationX;
- }
- };
-
- public static final FloatProperty<TaskView> SNAPSHOT_SCALE =
- new FloatProperty<>("snapshotScale") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.setSnapshotScale(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mTaskThumbnailViewDeprecated.getScaleX();
- }
- };
-
- public TaskViewData mTaskViewData = new TaskViewData();
- protected TaskThumbnailViewDeprecated mTaskThumbnailViewDeprecated;
- protected TaskThumbnailView mTaskThumbnailView;
- protected TaskViewIcon mIconView;
- protected final DigitalWellBeingToast mDigitalWellBeingToast;
- protected float mFullscreenProgress;
- private float mGridProgress;
- protected float mTaskThumbnailSplashAlpha;
- private float mNonGridScale = 1;
- private float mDismissScale = 1;
- protected final FullscreenDrawParams mCurrentFullscreenParams;
- protected final RecentsViewContainer mContainer;
-
- // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
- private float mDismissTranslationX;
- private float mDismissTranslationY;
- private float mTaskOffsetTranslationX;
- private float mTaskOffsetTranslationY;
- private float mTaskResistanceTranslationX;
- private float mTaskResistanceTranslationY;
- // The following translation variables should only be used in the same orientation as Launcher.
- private float mBoxTranslationY;
- // The following grid translations scales with mGridProgress.
- private float mGridTranslationX;
- private float mGridTranslationY;
- // The following grid translation is used to animate closing the gap between grid and clear all.
- private float mGridEndTranslationX;
- // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
- // switch.
- private float mNonGridTranslationX;
- private float mNonGridPivotTranslationX;
- // Used when in SplitScreenSelectState
- private float mSplitSelectTranslationY;
- private float mSplitSelectTranslationX;
-
- @Nullable
- private ObjectAnimator mIconAndDimAnimator;
- private float mIconScaleAnimStartProgress = 0;
- private float mFocusTransitionProgress = 1;
- private float mModalness = 0;
- private float mStableAlpha = 1;
-
- private int mTaskViewId = -1;
- protected List<TaskContainer> mTaskContainers = Collections.emptyList();
-
- private boolean mShowScreenshot;
- private boolean mBorderEnabled;
-
- // The current background requests to load the task thumbnail and icon
- @Nullable
- private CancellableTask mThumbnailLoadRequest;
- @Nullable
- private CancellableTask mIconLoadRequest;
-
- private boolean mEndQuickswitchCuj;
-
- private final float[] mIconCenterCoords = new float[2];
-
- protected final PointF mLastTouchDownPosition = new PointF();
-
- private boolean mIsClickableAsLiveTile = true;
-
- @Nullable
- private final BorderAnimator mFocusBorderAnimator;
-
- @Nullable
- private final BorderAnimator mHoverBorderAnimator;
-
- public TaskView(Context context) {
- this(context, null);
- }
-
- public TaskView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- this(context, attrs, defStyleAttr, defStyleRes, null, null);
- }
-
- @VisibleForTesting
- public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
- int defStyleRes, BorderAnimator focusBorderAnimator,
- BorderAnimator hoverBorderAnimator) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mContainer = RecentsViewContainer.containerFromContext(context);
- setOnClickListener(this::onClick);
-
- mCurrentFullscreenParams = new FullscreenDrawParams(context);
- mDigitalWellBeingToast = new DigitalWellBeingToast(mContainer, this);
-
- boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
- || Flags.enableFocusOutline();
- boolean cursorHoverStatesEnabled = enableCursorHoverStates();
-
- setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled);
-
- TypedArray styledAttrs = context.obtainStyledAttributes(
- attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
-
- if (focusBorderAnimator != null) {
- mFocusBorderAnimator = focusBorderAnimator;
- } else {
- mFocusBorderAnimator = keyboardFocusHighlightEnabled
- ? BorderAnimator.createSimpleBorderAnimator(
- /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
- /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_border_width),
- /* boundsBuilder= */ this::getThumbnailBounds,
- /* targetView= */ this,
- /* borderColor= */ styledAttrs.getColor(
- R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR))
- : null;
- }
-
- if (hoverBorderAnimator != null) {
- mHoverBorderAnimator = hoverBorderAnimator;
- } else {
- mHoverBorderAnimator = cursorHoverStatesEnabled
- ? BorderAnimator.createSimpleBorderAnimator(
- /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
- /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
- R.dimen.task_hover_border_width),
- /* boundsBuilder= */ this::getThumbnailBounds,
- /* targetView= */ this,
- /* borderColor= */ styledAttrs.getColor(
- R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
- : null;
- }
- styledAttrs.recycle();
- }
-
- /** Returns the thumbnail's bounds relative to this view. */
- public Unit getThumbnailBounds(@NonNull Rect bounds) {
- return getThumbnailBounds(bounds, false);
- }
-
- /** Returns the thumbnail's bounds, optionally relative to the screen. */
- public Unit getThumbnailBounds(@NonNull Rect bounds, boolean relativeToDragLayer) {
- View snapshotView = getSnapshotView();
-
- if (relativeToDragLayer) {
- mContainer.getDragLayer().getDescendantRectRelativeToSelf(snapshotView, bounds);
- } else {
- bounds.set(snapshotView.getLeft() + Math.round(snapshotView.getTranslationX()),
- snapshotView.getTop() + Math.round(snapshotView.getTranslationY()),
- snapshotView.getRight() + Math.round(snapshotView.getTranslationX()),
- snapshotView.getBottom() + Math.round(snapshotView.getTranslationY()));
- }
- return Unit.INSTANCE;
- }
-
- public void setTaskViewId(int id) {
- this.mTaskViewId = id;
- }
-
- public int getTaskViewId() {
- return mTaskViewId;
- }
-
- /**
- * Updates [TaskThumbnailView] to reflect the latest [Task] state (i.e., task isRunning).
- */
- public void notifyIsRunningTaskUpdated() {
- // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
- // so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
- if (!mTaskContainers.isEmpty()) {
- bindTaskThumbnailView();
- }
- }
-
- /**
- * Builds proto for logging.
- *
- * @deprecated Use {@link #getItemInfo(Task)} instead.
- */
- @Deprecated
- public WorkspaceItemInfo getItemInfo() {
- return getItemInfo(getFirstTask());
- }
-
- /**
- * Builds proto for logging
- */
- @VisibleForTesting
- public WorkspaceItemInfo getItemInfo(@Nullable Task task) {
- WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
- stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
- stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
- if (task == null) {
- return stubInfo;
- }
-
- ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
- stubInfo.user = componentKey.user;
- stubInfo.intent = new Intent().setComponent(componentKey.componentName);
- stubInfo.title = task.title;
- if (getRecentsView() != null) {
- stubInfo.screenId = getRecentsView().indexOfChild(this);
- }
- if (Flags.privateSpaceRestrictAccessibilityDrag()) {
- if (UserCache.getInstance(getContext()).getUserInfo(componentKey.user).isPrivate()) {
- stubInfo.runtimeStatusFlags |= FLAG_NOT_PINNABLE;
- }
- }
- return stubInfo;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTaskThumbnailViewDeprecated = findViewById(R.id.snapshot);
- if (enableRefactorTaskThumbnail()) {
- mTaskThumbnailView = new TaskThumbnailView(mContext);
- mTaskThumbnailView.setLayoutParams(mTaskThumbnailViewDeprecated.getLayoutParams());
- int indexOfSnapshotView = indexOfChild(mTaskThumbnailViewDeprecated);
- addView(mTaskThumbnailView, indexOfSnapshotView);
- mTaskThumbnailViewDeprecated.setVisibility(View.GONE);
- }
- ViewStub iconViewStub = findViewById(R.id.icon);
- if (enableOverviewIconMenu()) {
- iconViewStub.setLayoutResource(R.layout.icon_app_chip_view);
- } else {
- iconViewStub.setLayoutResource(R.layout.icon_view);
- }
- mIconView = (TaskViewIcon) iconViewStub.inflate();
- mIconTouchDelegate = new TransformingTouchDelegate(mIconView.asView());
- }
-
- @Override
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
- public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- if (mFocusBorderAnimator != null && mBorderEnabled) {
- mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true);
- }
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- if (mHoverBorderAnimator != null && mBorderEnabled) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- mHoverBorderAnimator.setBorderVisibility(/* visible= */ true, /* animated= */
- true);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- mHoverBorderAnimator.setBorderVisibility(/* visible= */ false, /* animated= */
- true);
- break;
- default:
- break;
- }
- }
- return super.onHoverEvent(event);
- }
-
- /**
- * Enable or disable showing border on hover and focus change
- */
- public void setBorderEnabled(boolean enabled) {
- if (mBorderEnabled == enabled) {
- return;
- }
-
- mBorderEnabled = enabled;
- // Set the animation correctly in case it misses the hover/focus event during state
- // transition
- if (mHoverBorderAnimator != null) {
- mHoverBorderAnimator.setBorderVisibility(/* visible= */
- enabled && isHovered(), /* animated= */ true);
- }
-
- if (mFocusBorderAnimator != null) {
- mFocusBorderAnimator.setBorderVisibility(/* visible= */
- enabled && isFocused(), /* animated= */true);
- }
- }
-
- @Override
- public boolean onInterceptHoverEvent(MotionEvent event) {
- if (enableCursorHoverStates()) {
- // avoid triggering hover event on child elements which would cause HOVER_EXIT for this
- // task view
- return true;
- } else {
- return super.onInterceptHoverEvent(event);
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- // Draw border first so any child views outside of the thumbnail bounds are drawn above it.
- if (mFocusBorderAnimator != null) {
- mFocusBorderAnimator.drawBorder(canvas);
- }
- if (mHoverBorderAnimator != null) {
- mHoverBorderAnimator.drawBorder(canvas);
- }
- super.draw(canvas);
- }
-
- /**
- * Whether the taskview should take the touch event from parent. Events passed to children
- * that might require special handling.
- */
- public boolean offerTouchToChildren(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- computeAndSetIconTouchDelegate(mIconView, mIconCenterCoords, mIconTouchDelegate);
- }
- return mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event);
- }
-
- protected void computeAndSetIconTouchDelegate(TaskViewIcon view, float[] tempCenterCoords,
- TransformingTouchDelegate transformingTouchDelegate) {
- if (view == null) {
- return;
- }
- float viewHalfWidth = view.getWidth() / 2f;
- float viewHalfHeight = view.getHeight() / 2f;
- tempCenterCoords[0] = viewHalfWidth;
- tempCenterCoords[1] = viewHalfHeight;
- getDescendantCoordRelativeToAncestor(view.asView(), mContainer.getDragLayer(),
- tempCenterCoords, false);
- transformingTouchDelegate.setBounds(
- (int) (tempCenterCoords[0] - viewHalfWidth),
- (int) (tempCenterCoords[1] - viewHalfHeight),
- (int) (tempCenterCoords[0] + viewHalfWidth),
- (int) (tempCenterCoords[1] + viewHalfHeight));
- }
-
- /**
- * The modalness of this view is how it should be displayed when it is shown on its own in the
- * modal state of overview.
- *
- * @param modalness [0, 1] 0 being in context with other tasks, 1 being shown on its own.
- */
- public void setModalness(float modalness) {
- if (mModalness == modalness) {
- return;
- }
- mModalness = modalness;
- mIconView.setModalAlpha(1 - modalness);
- mDigitalWellBeingToast.updateBannerOffset(modalness);
- }
-
- public DigitalWellBeingToast getDigitalWellBeingToast() {
- return mDigitalWellBeingToast;
- }
-
- /**
- * Updates this task view to the given {@param task}.
- */
- public void bind(Task task, RecentsOrientedState orientedState) {
- cancelPendingLoadTasks();
- setupTaskContainers(task);
- setOrientationState(orientedState);
- }
-
- protected void setupTaskContainers(Task task) {
- mTaskContainers = Collections.singletonList(
- new TaskContainer(task, mTaskThumbnailViewDeprecated, mIconView,
- STAGE_POSITION_UNDEFINED, mDigitalWellBeingToast));
- if (enableRefactorTaskThumbnail()) {
- bindTaskThumbnailView();
- } else {
- mTaskThumbnailViewDeprecated.bind(task);
- }
- }
-
- private void bindTaskThumbnailView() {
- // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
- // so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
- mTaskThumbnailView.getViewModel().bind(new TaskThumbnail(getFirstTask(), isRunningTask()));
- }
-
- /**
- * Sets up an on-click listener and the visibility for show_windows icon on top of the task.
- */
- public void setUpShowAllInstancesListener() {
- if (mTaskContainers.isEmpty()) {
- return;
- }
- String taskPackageName = mTaskContainers.get(0).getTask().key.getPackageName();
-
- // icon of the top/left task
- View showWindowsView = findViewById(R.id.show_windows);
- updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName));
- }
-
- /**
- * Returns a callback that updates the state of the filter and the recents overview
- *
- * @param taskPackageName package name of the task to filter by
- */
- @Nullable
- protected View.OnClickListener getFilterUpdateCallback(String taskPackageName) {
- View.OnClickListener cb = (view) -> {
- // update and apply a new filter
- getRecentsView().setAndApplyFilter(taskPackageName);
- };
-
- if (!getRecentsView().getFilterState().shouldShowFilterUI(taskPackageName)) {
- cb = null;
- }
- return cb;
- }
-
- /**
- * Sets the correct visibility and callback on the provided filterView based on whether
- * the callback is null or not
- */
- protected void updateFilterCallback(@NonNull View filterView,
- @Nullable View.OnClickListener callback) {
- // Filtering changes alpha instead of the visibility since visibility
- // can be altered separately through RecentsView#resetFromSplitSelectionState()
- if (callback == null) {
- filterView.setAlpha(0);
- } else {
- filterView.setAlpha(1);
- }
-
- filterView.setOnClickListener(callback);
- }
-
- /**
- * Returns a list of all TaskContainers in the TaskView.
- */
- public List<TaskContainer> getTaskContainers() {
- return mTaskContainers;
- }
-
- /**
- * Returns the first task bound to this TaskView.
- *
- * @deprecated Use {@link #mTaskContainers} instead.
- */
- @Deprecated
- @Nullable
- public Task getFirstTask() {
- return !mTaskContainers.isEmpty() ? mTaskContainers.get(0).getTask() : null;
- }
-
- /**
- * Check if given {@code taskId} is tracked in this view
- */
- public boolean containsTaskId(int taskId) {
- return getTaskContainerById(taskId) != null;
- }
-
- /**
- * Returns a copy of integer array containing taskIds of all tasks in the TaskView.
- */
- public int[] getTaskIds() {
- return mTaskContainers.stream().mapToInt(
- container -> container.getTask().key.id).toArray();
- }
-
- public boolean containsMultipleTasks() {
- return mTaskContainers.size() > 1;
- }
-
- /**
- * Returns the TaskContainer corresponding to a given taskId, or null if the TaskView does
- * not contain a Task with that ID.
- */
- @Nullable
- public TaskContainer getTaskContainerById(int taskId) {
- return mTaskContainers.stream().filter(
- container -> container.getTask().key.id == taskId).findFirst().orElse(null);
- }
-
- /**
- * Returns the first thumbnailView of the TaskView.
- *
- * @deprecated Use {@link #mTaskContainers} instead.
- */
- @Deprecated
- public TaskThumbnailViewDeprecated getFirstThumbnailView() {
- return !mTaskContainers.isEmpty() ? mTaskContainers.get(0).getThumbnailView()
- : mTaskThumbnailViewDeprecated;
- }
-
- void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
- if (enableRefactorTaskThumbnail()) {
- // TODO(b/334825222) add thumbnail logic
- return;
- }
- if (getFirstTask() != null && thumbnailDatas != null) {
- final ThumbnailData thumbnailData = thumbnailDatas.get(getFirstTask().key.id);
- if (thumbnailData != null) {
- mTaskThumbnailViewDeprecated.setThumbnail(getFirstTask(), thumbnailData);
- return;
- }
- }
-
- mTaskThumbnailViewDeprecated.refresh();
- }
-
- public TaskThumbnailViewDeprecated[] getThumbnailViews() {
- return mTaskContainers.stream().map(
- TaskContainer::getThumbnailView).toArray(
- TaskThumbnailViewDeprecated[]::new);
- }
-
- /**
- * Returns the first iconView of the TaskView.
- *
- * @deprecated Use {@link #mTaskContainers} instead.
- */
- @Deprecated
- @Nullable
- public TaskViewIcon getFirstIconView() {
- return !mTaskContainers.isEmpty() ? mTaskContainers.get(0).getIconView() : null;
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- RecentsView recentsView = getRecentsView();
- if (recentsView == null || getFirstTask() == null) {
- return false;
- }
- SplitSelectStateController splitSelectStateController =
- recentsView.getSplitSelectController();
- // Disable taps for split selection animation unless we have multiple tasks
- boolean disableTapsForSplitSelect =
- splitSelectStateController.isSplitSelectActive()
- && splitSelectStateController.getInitialTaskId() == getFirstTask().key.id
- && !containsMultipleTasks();
- if (disableTapsForSplitSelect) {
- return false;
- }
-
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mLastTouchDownPosition.set(ev.getX(), ev.getY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- private void onClick(View view) {
- if (getFirstTask() == null) {
- Log.d("b/310064698", "onClick - task is null");
- return;
- }
- if (confirmSecondSplitSelectApp()) {
- Log.d("b/310064698",
- Arrays.toString(getTaskIds()) + " - onClick - split select is active");
- return;
- }
- RunnableList callbackList = launchTasks();
- Log.d("b/310064698",
- Arrays.toString(getTaskIds()) + " - onClick - callbackList: " + callbackList);
- if (callbackList != null) {
- callbackList.add(() -> Log.d("b/310064698",
- Arrays.toString(getTaskIds()) + " - onClick - launchCompleted"));
- }
- mContainer.getStatsLogManager().logger().withItemInfo(getItemInfo())
- .log(LAUNCHER_TASK_LAUNCH_TAP);
- }
-
- /**
- * @return {@code true} if user is already in split select mode and this tap was to choose the
- * second app. {@code false} otherwise
- */
- protected boolean confirmSecondSplitSelectApp() {
- int index = getLastSelectedChildTaskIndex();
- if (index >= mTaskContainers.size()) {
- return false;
- }
- TaskContainer container = mTaskContainers.get(index);
- if (container != null) {
- return getRecentsView().confirmSplitSelect(this, container.getTask(),
- container.getIconView().getDrawable(), container.getThumbnailView(),
- container.getThumbnailView().getThumbnail(), /* intent */ null,
- /* user */ null, container.getItemInfo());
- }
- return false;
- }
-
- /**
- * Returns the task index of the last selected child task (0 or 1).
- * If we contain multiple tasks and this TaskView is used as part of split selection, the
- * selected child task index will be that of the remaining task.
- */
- protected int getLastSelectedChildTaskIndex() {
- return 0;
- }
-
- /**
- * Starts the task associated with this view and animates the startup.
- *
- * @return CompletionStage to indicate the animation completion or null if the launch failed.
- */
- @Nullable
- public RunnableList launchTaskAnimated() {
- if (getFirstTask() != null) {
- TestLogging.recordEvent(
- TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", Arrays.toString(
- getTaskIds()));
- ActivityOptionsWrapper opts = mContainer.getActivityLaunchOptions(this, null);
- opts.options.setLaunchDisplayId(
- getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
- if (ActivityManagerWrapper.getInstance()
- .startActivityFromRecents(getFirstTask().key, opts.options)) {
- Log.d(TAG, "launchTaskAnimated - startActivityFromRecents: " + Arrays.toString(
- getTaskIds()));
- ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
- RecentsView recentsView = getRecentsView();
- if (recentsView.getRunningTaskViewId() != -1) {
- recentsView.onTaskLaunchedInLiveTileMode();
-
- // Return a fresh callback in the live tile case, so that it's not accidentally
- // triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
- RunnableList callbackList = new RunnableList();
- recentsView.addSideTaskLaunchCallback(callbackList);
- return callbackList;
- }
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- // If the recents transition is running (ie. in live tile mode), then the start
- // of a new task will merge into the existing transition and it currently will
- // not be run independently, so we need to rely on the onTaskAppeared() call
- // for the new task to trigger the side launch callback to flush this runnable
- // list (which is usually flushed when the app launch animation finishes)
- recentsView.addSideTaskLaunchCallback(opts.onEndCallback);
- }
- return opts.onEndCallback;
- } else {
- notifyTaskLaunchFailed(TAG);
- return null;
- }
- } else {
- Log.d(TAG, "launchTaskAnimated - getTask() is null" + Arrays.toString(getTaskIds()));
- return null;
- }
- }
-
- /**
- * Starts the task associated with this view without any animation
- */
- public void launchTask(@NonNull Consumer<Boolean> callback) {
- launchTask(callback, false /* isQuickswitch */);
- }
-
- /**
- * Starts the task associated with this view without any animation
- */
- public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
- if (getFirstTask() != null) {
- TestLogging.recordEvent(
- TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", Arrays.toString(
- getTaskIds()));
-
- TaskRemovedDuringLaunchListener failureListener = new TaskRemovedDuringLaunchListener(
- getContext().getApplicationContext());
- if (isQuickswitch) {
- // We only listen for failures to launch in quickswitch because the during this
- // gesture launcher is in the background state, vs other launches which are in
- // the actual overview state
- failureListener.register(mContainer, getFirstTask().key.id, () -> {
- notifyTaskLaunchFailed(TAG);
- RecentsView rv = getRecentsView();
- if (rv != null) {
- // Disable animations for now, as it is an edge case and the app usually
- // covers launcher and also any state transition animation also gets
- // clobbered by QuickstepTransitionManager.createWallpaperOpenAnimations
- // when launcher shows again
- rv.startHome(false /* animated */);
- if (rv.mSizeStrategy.getTaskbarController() != null) {
- // LauncherTaskbarUIController depends on the launcher state when
- // checking whether to handle resume, but that can come in before
- // startHome() changes the state, so force-refresh here to ensure the
- // taskbar is updated
- rv.mSizeStrategy.getTaskbarController().refreshResumedState();
- }
- }
- });
- }
- // Indicate success once the system has indicated that the transition has started
- ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0,
- MAIN_EXECUTOR.getHandler(),
- elapsedRealTime -> callback.accept(true),
- elapsedRealTime -> failureListener.onTransitionFinished());
- opts.setLaunchDisplayId(
- getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
- if (isQuickswitch) {
- opts.setFreezeRecentTasksReordering();
- }
- // TODO(b/334826842) add splash functionality to new TTV
- if (!enableRefactorTaskThumbnail()) {
- opts.setDisableStartingWindow(mTaskThumbnailViewDeprecated.shouldShowSplashView());
- }
- Task.TaskKey key = getFirstTask().key;
- UI_HELPER_EXECUTOR.execute(() -> {
- if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
- // If the call to start activity failed, then post the result immediately,
- // otherwise, wait for the animation start callback from the activity options
- // above
- MAIN_EXECUTOR.post(() -> {
- notifyTaskLaunchFailed(TAG);
- callback.accept(false);
- });
- }
- Log.d(TAG,
- "launchTask - startActivityFromRecents: " + Arrays.toString(getTaskIds()));
- });
- } else {
- callback.accept(false);
- Log.d(TAG, "launchTask - getTask() is null" + Arrays.toString(getTaskIds()));
- }
- }
-
- /**
- * Launch of the current task (both live and inactive tasks) with an animation.
- */
- @Nullable
- public RunnableList launchTasks() {
- RecentsView recentsView = getRecentsView();
- RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
- if (isRunningTask() && remoteTargetHandles != null) {
- if (!mIsClickableAsLiveTile) {
- Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.");
- return null;
- }
-
- mIsClickableAsLiveTile = false;
- RemoteAnimationTargets targets;
- if (remoteTargetHandles.length == 1) {
- targets = remoteTargetHandles[0].getTransformParams().getTargetSet();
- } else {
- RemoteAnimationTarget[] apps = Arrays.stream(remoteTargetHandles)
- .flatMap(handle -> Stream.of(
- handle.getTransformParams().getTargetSet().apps))
- .toArray(RemoteAnimationTarget[]::new);
- RemoteAnimationTarget[] wallpapers = Arrays.stream(remoteTargetHandles)
- .flatMap(handle -> Stream.of(
- handle.getTransformParams().getTargetSet().wallpapers))
- .toArray(RemoteAnimationTarget[]::new);
- targets = new RemoteAnimationTargets(apps, wallpapers,
- remoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
- remoteTargetHandles[0].getTransformParams().getTargetSet().targetMode);
- }
- if (targets == null) {
- // If the recents animation is cancelled somehow between the parent if block and
- // here, try to launch the task as a non live tile task.
- RunnableList runnableList = launchTaskAnimated();
- if (runnableList == null) {
- Log.e(TAG, "Recents animation cancelled and cannot launch task as non-live tile"
- + "; returning to home");
- }
- mIsClickableAsLiveTile = true;
- return runnableList;
- }
-
- RunnableList runnableList = new RunnableList();
- AnimatorSet anim = new AnimatorSet();
- TaskViewUtils.composeRecentsLaunchAnimator(
- anim, this, targets.apps,
- targets.wallpapers, targets.nonApps, true /* launcherClosing */,
- recentsView.getStateManager(), recentsView,
- recentsView.getDepthController());
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- if (getFirstTask() != null
- && getFirstTask().key.displayId != getRootViewDisplayId()) {
- launchTaskAnimated();
- }
- mIsClickableAsLiveTile = true;
- runEndCallback();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- runEndCallback();
- }
-
- private void runEndCallback() {
- runnableList.executeAllAndDestroy();
- }
- });
- anim.start();
- Log.d(TAG, "launchTasks - composeRecentsLaunchAnimator: " + Arrays.toString(
- getTaskIds()));
- recentsView.onTaskLaunchedInLiveTileMode();
- return runnableList;
- } else {
- return launchTaskAnimated();
- }
- }
-
- /**
- * See {@link TaskDataChanges}
- *
- * @param visible If this task view will be visible to the user in overview or hidden
- */
- public void onTaskListVisibilityChanged(boolean visible) {
- onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL);
- }
-
- /**
- * See {@link TaskDataChanges}
- *
- * @param visible If this task view will be visible to the user in overview or hidden
- */
- public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
- if (getFirstTask() == null) {
- return;
- }
- cancelPendingLoadTasks();
- if (visible) {
- // These calls are no-ops if the data is already loaded, try and load the high
- // resolution thumbnail if the state permits
- RecentsModel model = RecentsModel.INSTANCE.get(getContext());
- TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
- TaskIconCache iconCache = model.getIconCache();
-
- if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
- mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
- getFirstTask(), thumbnail -> {
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334825222) add thumbnail state
- mTaskThumbnailViewDeprecated.setThumbnail(getFirstTask(),
- thumbnail);
- }
- });
- }
- if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
- mIconLoadRequest = iconCache.updateIconInBackground(getFirstTask(),
- (task) -> {
- setIcon(mIconView, task.icon);
- if (enableOverviewIconMenu()) {
- setText(mIconView, task.title);
- }
- mDigitalWellBeingToast.initialize(task);
- });
- }
- if (needsUpdate(changes, FLAG_UPDATE_CORNER_RADIUS)) {
- mCurrentFullscreenParams.updateCornerRadius(getContext());
- }
- } else {
- if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334825222) add thumbnail state
- mTaskThumbnailViewDeprecated.setThumbnail(null, null);
- }
- // Reset the task thumbnail reference as well (it will be fetched from the cache or
- // reloaded next time we need it)
- getFirstTask().thumbnail = null;
- }
- if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
- setIcon(mIconView, null);
- if (enableOverviewIconMenu()) {
- setText(mIconView, null);
- }
- }
- }
- }
-
- protected boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
- return (dataChange & flag) == flag;
- }
-
- protected void cancelPendingLoadTasks() {
- if (mThumbnailLoadRequest != null) {
- mThumbnailLoadRequest.cancel();
- mThumbnailLoadRequest = null;
- }
- if (mIconLoadRequest != null) {
- mIconLoadRequest.cancel();
- mIconLoadRequest = null;
- }
- }
-
- private boolean showTaskMenu(TaskViewIcon iconView) {
- if (!getRecentsView().canLaunchFullscreenTask()) {
- // Don't show menu when selecting second split screen app
- return true;
- }
-
- if (!mContainer.getDeviceProfile().isTablet
- && !getRecentsView().isClearAllHidden()) {
- getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
- return false;
- } else {
- mContainer.getStatsLogManager().logger().withItemInfo(getItemInfo())
- .log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
- return showTaskMenuWithContainer(iconView);
- }
- }
-
- protected boolean showTaskMenuWithContainer(TaskViewIcon iconView) {
- Optional<TaskContainer> menuContainer = mTaskContainers.stream().filter(
- container -> container.getIconView() == iconView).findAny();
- if (menuContainer.isEmpty()) {
- return false;
- }
- DeviceProfile dp = mContainer.getDeviceProfile();
- if (enableOverviewIconMenu() && iconView instanceof IconAppChipView) {
- ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ true);
- return TaskMenuView.showForTask(menuContainer.get(),
- () -> ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false));
- } else if (dp.isTablet) {
- int alignedOptionIndex = 0;
- if (getRecentsView().isOnGridBottomRow(menuContainer.get().getTaskView())
- && dp.isLandscape) {
- if (Flags.enableGridOnlyOverview()) {
- // With no focused task, there is less available space below the tasks, so align
- // the arrow to the third option in the menu.
- alignedOptionIndex = 2;
- } else {
- // Bottom row of landscape grid aligns arrow to second option to avoid clipping
- alignedOptionIndex = 1;
- }
- }
- return TaskMenuViewWithArrow.Companion.showForTask(menuContainer.get(),
- alignedOptionIndex);
- } else {
- return TaskMenuView.showForTask(menuContainer.get());
- }
- }
-
- protected void setIcon(TaskViewIcon iconView, @Nullable Drawable icon) {
- if (icon != null) {
- iconView.setDrawable(icon);
- iconView.setOnClickListener(v -> {
- if (confirmSecondSplitSelectApp()) {
- return;
- }
- showTaskMenu(iconView);
- });
- iconView.setOnLongClickListener(v -> {
- requestDisallowInterceptTouchEvent(true);
- return showTaskMenu(iconView);
- });
- } else {
- iconView.setDrawable(null);
- iconView.setOnClickListener(null);
- iconView.setOnLongClickListener(null);
- }
- }
-
- protected void setText(TaskViewIcon iconView, CharSequence text) {
- iconView.setText(text);
- }
-
- public void setOrientationState(RecentsOrientedState orientationState) {
- mIconView.setIconOrientation(orientationState, isGridTask());
- setThumbnailOrientation(orientationState);
- }
-
- protected void setThumbnailOrientation(RecentsOrientedState orientationState) {
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-
- // TODO(b/271468547), we should default to setting trasnlations only on the snapshot instead
- // of a hybrid of both margins and translations
- LayoutParams snapshotParams = (LayoutParams) getSnapshotView().getLayoutParams();
- snapshotParams.topMargin = thumbnailTopMargin;
- getSnapshotView().setLayoutParams(snapshotParams);
-
- // TODO(b/335606129) Investigate the usage of [TaskOverlay] in the new TaskThumbnailView.
- // and if it's still necessary we should support that in the new TTV class.
- if (!enableRefactorTaskThumbnail()) {
- mTaskThumbnailViewDeprecated.getTaskOverlay().updateOrientationState(orientationState);
- }
- mDigitalWellBeingToast.initialize(getFirstTask());
- }
-
- /**
- * Returns whether the task is part of overview grid and not being focused.
- */
- public boolean isGridTask() {
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- return deviceProfile.isTablet && !isFocusedTask();
- }
-
- /**
- * Called to animate a smooth transition when going directly from an app into Overview (and
- * vice versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
- */
- protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
- if (invert) {
- progress = 1 - progress;
- }
- mFocusTransitionProgress = progress;
- float iconScalePercentage = (float) SCALE_ICON_DURATION / DIM_ANIM_DURATION;
- float lowerClamp = invert ? 1f - iconScalePercentage : 0;
- float upperClamp = invert ? 1 : iconScalePercentage;
- float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
- .getInterpolation(progress);
- mIconView.setContentAlpha(scale);
- mDigitalWellBeingToast.updateBannerOffset(1f - scale);
- }
-
- public void setIconScaleAnimStartProgress(float startProgress) {
- mIconScaleAnimStartProgress = startProgress;
- }
-
- public void animateIconScaleAndDimIntoView() {
- if (mIconAndDimAnimator != null) {
- mIconAndDimAnimator.cancel();
- }
- mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1);
- mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress);
- mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR);
- mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIconAndDimAnimator = null;
- }
- });
- mIconAndDimAnimator.start();
- }
-
- protected void setIconScaleAndDim(float iconScale) {
- setIconScaleAndDim(iconScale, false);
- }
-
- private void setIconScaleAndDim(float iconScale, boolean invert) {
- if (mIconAndDimAnimator != null) {
- mIconAndDimAnimator.cancel();
- }
- setIconsAndBannersTransitionProgress(iconScale, invert);
- }
-
- protected void resetPersistentViewTransforms() {
- mNonGridTranslationX = mGridTranslationX =
- mGridTranslationY = mBoxTranslationY = mNonGridPivotTranslationX = 0f;
- resetViewTransforms();
- }
-
- protected void resetViewTransforms() {
- // fullscreenTranslation and accumulatedTranslation should not be reset, as
- // resetViewTransforms is called during Quickswitch scrolling.
- mDismissTranslationX = mTaskOffsetTranslationX =
- mTaskResistanceTranslationX = mSplitSelectTranslationX = mGridEndTranslationX = 0f;
- mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
- if (getRecentsView() == null || !getRecentsView().isSplitSelectionActive()) {
- mSplitSelectTranslationY = 0f;
- }
-
- setSnapshotScale(1f);
- applyTranslationX();
- applyTranslationY();
- setTranslationZ(0);
- setAlpha(mStableAlpha);
- setIconScaleAndDim(1);
- setColorTint(0, 0);
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/335399428) add split select functionality to new TTV
- mTaskThumbnailViewDeprecated.resetViewTransforms();
- }
- }
-
- public void setStableAlpha(float parentAlpha) {
- mStableAlpha = parentAlpha;
- setAlpha(mStableAlpha);
- }
-
- @Override
- public void onRecycle() {
- resetPersistentViewTransforms();
- // Clear any references to the thumbnail (it will be re-read either from the cache or the
- // system on next bind)
- // TODO(b/334825222): Implement thumbnail/snapshot for the new [TaskThumbnailView].
- if (enableRefactorTaskThumbnail()) {
- notifyIsRunningTaskUpdated();
- } else {
- mTaskThumbnailViewDeprecated.setThumbnail(getFirstTask(), null);
- }
- setOverlayEnabled(false);
- onTaskListVisibilityChanged(false);
- mBorderEnabled = false;
- }
-
- public float getTaskCornerRadius() {
- return mCurrentFullscreenParams.mCornerRadius;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
- if (deviceProfile.isTablet) {
- setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : right - left);
- setPivotY(thumbnailTopMargin);
- } else {
- setPivotX((right - left) * 0.5f);
- setPivotY(thumbnailTopMargin + (getHeight() - thumbnailTopMargin) * 0.5f);
- }
- SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
- setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
- }
-
- /**
- * How much to scale down pages near the edge of the screen.
- */
- public static float getEdgeScaleDownFactor(DeviceProfile deviceProfile) {
- return deviceProfile.isTablet ? EDGE_SCALE_DOWN_FACTOR_GRID
- : EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
- }
-
- private void setNonGridScale(float nonGridScale) {
- mNonGridScale = nonGridScale;
- applyScale();
- }
-
- public float getNonGridScale() {
- return mNonGridScale;
- }
-
- private void setSnapshotScale(float dismissScale) {
- mDismissScale = dismissScale;
- applyScale();
- }
-
- /**
- * Moves TaskView between carousel and 2 row grid.
- *
- * @param gridProgress 0 = carousel; 1 = 2 row grid.
- */
- public void setGridProgress(float gridProgress) {
- mGridProgress = gridProgress;
- applyTranslationX();
- applyTranslationY();
- applyScale();
- }
-
- private void applyScale() {
- float scale = 1;
- scale *= getPersistentScale();
- scale *= mDismissScale;
- setScaleX(scale);
- setScaleY(scale);
- if (enableRefactorTaskThumbnail()) {
- mTaskViewData.getScale().setValue(scale);
- }
- updateSnapshotRadius();
- }
-
- /**
- * Returns multiplication of scale that is persistent (e.g. fullscreen and grid), and does not
- * change according to a temporary state.
- */
- public float getPersistentScale() {
- float scale = 1;
- scale *= Utilities.mapRange(mGridProgress, mNonGridScale, 1f);
- return scale;
- }
-
- /**
- * Updates alpha of task thumbnail splash on swipe up/down.
- */
- public void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) {
- mTaskThumbnailSplashAlpha = taskThumbnailSplashAlpha;
- applyThumbnailSplashAlpha();
- }
-
- protected void applyThumbnailSplashAlpha() {
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334826842) add splash functionality to new TTV
- mTaskThumbnailViewDeprecated.setSplashAlpha(mTaskThumbnailSplashAlpha);
- }
- }
-
- protected void refreshTaskThumbnailSplash() {
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334826842) add splash functionality to new TTV
- mTaskThumbnailViewDeprecated.refreshSplashView();
- }
- }
-
- private void setSplitSelectTranslationX(float x) {
- mSplitSelectTranslationX = x;
- applyTranslationX();
- }
-
- private void setSplitSelectTranslationY(float y) {
- mSplitSelectTranslationY = y;
- applyTranslationY();
- }
-
- private void setDismissTranslationX(float x) {
- mDismissTranslationX = x;
- applyTranslationX();
- }
-
- private void setDismissTranslationY(float y) {
- mDismissTranslationY = y;
- applyTranslationY();
- }
-
- private void setTaskOffsetTranslationX(float x) {
- mTaskOffsetTranslationX = x;
- applyTranslationX();
- }
-
- private void setTaskOffsetTranslationY(float y) {
- mTaskOffsetTranslationY = y;
- applyTranslationY();
- }
-
- private void setTaskResistanceTranslationX(float x) {
- mTaskResistanceTranslationX = x;
- applyTranslationX();
- }
-
- private void setTaskResistanceTranslationY(float y) {
- mTaskResistanceTranslationY = y;
- applyTranslationY();
- }
-
- public float getNonGridTranslationX() {
- return mNonGridTranslationX;
- }
-
- /**
- * Updates X coordinate of non-grid translation.
- */
- public void setNonGridTranslationX(float nonGridTranslationX) {
- mNonGridTranslationX = nonGridTranslationX;
- applyTranslationX();
- }
-
- public void setGridTranslationX(float gridTranslationX) {
- mGridTranslationX = gridTranslationX;
- applyTranslationX();
- }
-
- public float getGridTranslationX() {
- return mGridTranslationX;
- }
-
- public void setGridTranslationY(float gridTranslationY) {
- mGridTranslationY = gridTranslationY;
- applyTranslationY();
- }
-
- public float getGridTranslationY() {
- return mGridTranslationY;
- }
-
- private void setGridEndTranslationX(float gridEndTranslationX) {
- mGridEndTranslationX = gridEndTranslationX;
- applyTranslationX();
- }
-
- /**
- * Set translation X for non-grid pivot
- */
- public void setNonGridPivotTranslationX(float nonGridPivotTranslationX) {
- mNonGridPivotTranslationX = nonGridPivotTranslationX;
- applyTranslationX();
- }
-
- public float getScrollAdjustment(boolean gridEnabled) {
- float scrollAdjustment = 0;
- if (gridEnabled) {
- scrollAdjustment += mGridTranslationX;
- } else {
- scrollAdjustment += getNonGridTranslationX();
- }
- return scrollAdjustment;
- }
-
- public float getOffsetAdjustment(boolean gridEnabled) {
- return getScrollAdjustment(gridEnabled);
- }
-
- public float getSizeAdjustment(boolean fullscreenEnabled) {
- float sizeAdjustment = 1;
- if (fullscreenEnabled) {
- sizeAdjustment *= mNonGridScale;
- }
- return sizeAdjustment;
- }
-
- private void setBoxTranslationY(float boxTranslationY) {
- mBoxTranslationY = boxTranslationY;
- applyTranslationY();
- }
-
- private void applyTranslationX() {
- setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
- + mSplitSelectTranslationX + mGridEndTranslationX + getPersistentTranslationX());
- }
-
- private void applyTranslationY() {
- setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
- + mSplitSelectTranslationY + getPersistentTranslationY());
- }
-
- /**
- * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does not
- * change according to a temporary state (e.g. task offset).
- */
- public float getPersistentTranslationX() {
- return getNonGridTrans(mNonGridTranslationX) + getGridTrans(mGridTranslationX)
- + getNonGridTrans(mNonGridPivotTranslationX);
- }
-
- /**
- * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does not
- * change according to a temporary state (e.g. task offset).
- */
- public float getPersistentTranslationY() {
- return mBoxTranslationY + getGridTrans(mGridTranslationY);
- }
-
- public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
- return getPagedOrientationHandler().getPrimaryValue(
- SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
- }
-
- public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
- return getPagedOrientationHandler().getSecondaryValue(
- SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
- }
-
- public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
- return getPagedOrientationHandler().getPrimaryValue(
- DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
- }
-
- public FloatProperty<TaskView> getSecondaryDismissTranslationProperty() {
- return getPagedOrientationHandler().getSecondaryValue(
- DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
- }
-
- public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
- return getPagedOrientationHandler().getPrimaryValue(
- TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
- }
-
- public FloatProperty<TaskView> getSecondaryTaskOffsetTranslationProperty() {
- return getPagedOrientationHandler().getSecondaryValue(
- TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
- }
-
- public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
- return getPagedOrientationHandler().getSecondaryValue(
- TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
- return false;
- }
-
- public boolean isEndQuickswitchCuj() {
- return mEndQuickswitchCuj;
- }
-
- public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
- mEndQuickswitchCuj = endQuickswitchCuj;
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
-
- info.addAction(
- new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close,
- getContext().getText(R.string.accessibility_close)));
-
- final Context context = getContext();
- for (TaskContainer taskContainer : mTaskContainers) {
- for (SystemShortcut s : TraceHelper.allowIpcs(
- "TV.a11yInfo", () -> getEnabledShortcuts(this, taskContainer))) {
- info.addAction(s.createAccessibilityAction(context));
- }
- }
-
- if (mDigitalWellBeingToast.hasLimit()) {
- info.addAction(
- new AccessibilityNodeInfo.AccessibilityAction(
- R.string.accessibility_app_usage_settings,
- getContext().getText(R.string.accessibility_app_usage_settings)));
- }
-
- final RecentsView recentsView = getRecentsView();
- final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
- AccessibilityNodeInfo.CollectionItemInfo.obtain(
- 0, 1, recentsView.getTaskViewCount() - recentsView.indexOfChild(this) - 1,
- 1, false);
- info.setCollectionItemInfo(itemInfo);
- }
-
- @Override
- public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (action == R.string.accessibility_close) {
- getRecentsView().dismissTask(this, true /*animateTaskView*/,
- true /*removeTask*/);
- return true;
- }
-
- if (action == R.string.accessibility_app_usage_settings) {
- mDigitalWellBeingToast.openAppUsageSettings(this);
- return true;
- }
-
- for (TaskContainer taskContainer : mTaskContainers) {
- for (SystemShortcut s : getEnabledShortcuts(this,
- taskContainer)) {
- if (s.hasHandlerForAction(action)) {
- s.onClick(this);
- return true;
- }
- }
- }
-
- return super.performAccessibilityAction(action, arguments);
- }
-
- @Nullable
- public RecentsView<?, ?> getRecentsView() {
- return (RecentsView<?, ?>) getParent();
- }
-
- RecentsPagedOrientationHandler getPagedOrientationHandler() {
- return getRecentsView().mOrientationState.getOrientationHandler();
- }
-
- private void notifyTaskLaunchFailed(String tag) {
- String msg = "Failed to launch task";
- if (getFirstTask() != null) {
- msg += " (task=" + getFirstTask().key.baseIntent + " userId="
- + getFirstTask().key.userId + ")";
- }
- Log.w(tag, msg);
- Toast.makeText(getContext(), R.string.activity_not_available, LENGTH_SHORT).show();
- }
-
- /**
- * Hides the icon and shows insets when this TaskView is about to be shown fullscreen.
- *
- * @param progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
- */
- public void setFullscreenProgress(float progress) {
- progress = Utilities.boundToRange(progress, 0, 1);
- mFullscreenProgress = progress;
- mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
- mTaskThumbnailViewDeprecated.getTaskOverlay().setFullscreenProgress(progress);
-
- RecentsView recentsView = mContainer.getOverviewPanel();
- // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
- // oversized and banner would look disproportionately large.
- if (recentsView.getStateManager().getState() != BACKGROUND_APP) {
- setIconsAndBannersTransitionProgress(progress, true);
- }
-
- updateSnapshotRadius();
- }
-
- protected void updateSnapshotRadius() {
- updateCurrentFullscreenParams();
- mTaskThumbnailViewDeprecated.setFullscreenParams(mCurrentFullscreenParams);
- }
-
- void updateCurrentFullscreenParams() {
- updateFullscreenParams(mCurrentFullscreenParams);
- }
-
- protected void updateFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
- if (getRecentsView() == null) {
- return;
- }
- fullscreenParams.setProgress(
- mFullscreenProgress, getRecentsView().getScaleX(), getScaleX());
- }
-
- /**
- * Updates TaskView scaling and translation required to support variable width if enabled, while
- * ensuring TaskView fits into screen in fullscreen.
- */
- void updateTaskSize() {
- ViewGroup.LayoutParams params = getLayoutParams();
- float nonGridScale;
- float boxTranslationY;
- int expectedWidth;
- int expectedHeight;
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- final int thumbnailPadding = deviceProfile.overviewTaskThumbnailTopMarginPx;
- final Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
- final int taskWidth = lastComputedTaskSize.width();
- final int taskHeight = lastComputedTaskSize.height();
- if (deviceProfile.isTablet) {
- int boxWidth;
- int boxHeight;
- boolean isFocusedTask = isFocusedTask();
- if (isFocusedTask) {
- // Task will be focused and should use focused task size. Use focusTaskRatio
- // that is associated with the original orientation of the focused task.
- boxWidth = taskWidth;
- boxHeight = taskHeight;
- } else {
- // Otherwise task is in grid, and should use lastComputedGridTaskSize.
- Rect lastComputedGridTaskSize = getRecentsView().getLastComputedGridTaskSize();
- boxWidth = lastComputedGridTaskSize.width();
- boxHeight = lastComputedGridTaskSize.height();
- }
-
- // Bound width/height to the box size.
- expectedWidth = boxWidth;
- expectedHeight = boxHeight + thumbnailPadding;
-
- // Scale to to fit task Rect.
- if (enableGridOnlyOverview()) {
- final Rect lastComputedCarouselTaskSize =
- getRecentsView().getLastComputedCarouselTaskSize();
- nonGridScale = lastComputedCarouselTaskSize.width() / (float) taskWidth;
- } else {
- nonGridScale = taskWidth / (float) boxWidth;
- }
-
- // Align to top of task Rect.
- boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
- } else {
- nonGridScale = 1f;
- boxTranslationY = 0f;
- expectedWidth = enableOverviewIconMenu() ? taskWidth : LayoutParams.MATCH_PARENT;
- expectedHeight = enableOverviewIconMenu()
- ? taskHeight + thumbnailPadding
- : LayoutParams.MATCH_PARENT;
- }
-
- setNonGridScale(nonGridScale);
- setBoxTranslationY(boxTranslationY);
- if (params.width != expectedWidth || params.height != expectedHeight) {
- params.width = expectedWidth;
- params.height = expectedHeight;
- setLayoutParams(params);
- }
- }
-
- private float getGridTrans(float endTranslation) {
- return Utilities.mapRange(mGridProgress, 0, endTranslation);
- }
-
- private float getNonGridTrans(float endTranslation) {
- return endTranslation - getGridTrans(endTranslation);
- }
-
- public boolean isRunningTask() {
- if (getRecentsView() == null) {
- return false;
- }
- return this == getRecentsView().getRunningTaskView();
- }
-
- public boolean isFocusedTask() {
- if (getRecentsView() == null) {
- return false;
- }
- return this == getRecentsView().getFocusedTaskView();
- }
-
- public void setShowScreenshot(boolean showScreenshot) {
- mShowScreenshot = showScreenshot;
- }
-
- public boolean showScreenshot() {
- if (!isRunningTask()) {
- return true;
- }
- return mShowScreenshot;
- }
-
- public void setOverlayEnabled(boolean overlayEnabled) {
- // TODO(b/335606129) Investigate the usage of [TaskOverlay] in the new TaskThumbnailView.
- // and if it's still necessary we should support that in the new TTV class.
- if (!enableRefactorTaskThumbnail()) {
- mTaskThumbnailViewDeprecated.setOverlayEnabled(overlayEnabled);
- }
- }
-
- public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition,
- getLogEventForPosition(splitPositionOption.stagePosition));
- }
-
- /**
- * Set a color tint on the snapshot and supporting views.
- */
- public void setColorTint(float amount, int tintColor) {
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334832108) Add scrim to new TTV
- mTaskThumbnailViewDeprecated.setDimAlpha(amount);
- }
- mIconView.setIconColorTint(tintColor, amount);
- mDigitalWellBeingToast.setBannerColorTint(tintColor, amount);
- }
-
-
- private int getRootViewDisplayId() {
- Display display = getRootView().getDisplay();
- return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
- }
-
- /**
- * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
- * IconView is unaffected.
- *
- * @param taskId is only used when setting visibility to a non-{@link View#VISIBLE} value
- */
- void setThumbnailVisibility(int visibility, int taskId) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != mIconView) {
- child.setVisibility(visibility);
- }
- }
- }
-
- private View getSnapshotView() {
- return enableRefactorTaskThumbnail() ? mTaskThumbnailView : mTaskThumbnailViewDeprecated;
- }
-
- /**
- * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
- */
- public static class FullscreenDrawParams implements SafeCloseable {
-
- private float mCornerRadius;
- private float mWindowCornerRadius;
-
- public float mCurrentDrawnCornerRadius;
-
- public FullscreenDrawParams(Context context) {
- updateCornerRadius(context);
- }
-
- /** Recomputes the start and end corner radius for the given Context. */
- public void updateCornerRadius(Context context) {
- mCornerRadius = computeTaskCornerRadius(context);
- mWindowCornerRadius = computeWindowCornerRadius(context);
- }
-
- @VisibleForTesting
- public float computeTaskCornerRadius(Context context) {
- return TaskCornerRadius.get(context);
- }
-
- @VisibleForTesting
- public float computeWindowCornerRadius(Context context) {
- return QuickStepContract.getWindowCornerRadius(context);
- }
-
- /**
- * Sets the progress in range [0, 1]
- */
- public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale) {
- mCurrentDrawnCornerRadius =
- Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius)
- / parentScale / taskViewScale;
- }
-
- @Override
- public void close() {
- }
- }
-
- /**
- * Holder for all Task dependent information.
- */
- public class TaskContainer {
- private final TaskThumbnailViewDeprecated mThumbnailView;
- private final Task mTask;
- private final TaskViewIcon mIconView;
- /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
- private @SplitConfigurationOptions.StagePosition int mStagePosition;
- @IdRes
- private final int mA11yNodeId;
- private final DigitalWellBeingToast mDigitalWellBeingToast;
-
- public TaskContainer(Task task, TaskThumbnailViewDeprecated thumbnailView,
- TaskViewIcon iconView, int stagePosition,
- DigitalWellBeingToast digitalWellBeingToast) {
- this.mTask = task;
- this.mThumbnailView = thumbnailView;
- this.mIconView = iconView;
- this.mStagePosition = stagePosition;
- this.mA11yNodeId = (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) ?
- R.id.split_bottomRight_appInfo : R.id.split_topLeft_appInfo;
- this.mDigitalWellBeingToast = digitalWellBeingToast;
- }
-
- public TaskThumbnailViewDeprecated getThumbnailView() {
- return mThumbnailView;
- }
-
- public Task getTask() {
- return mTask;
- }
-
- public WorkspaceItemInfo getItemInfo() {
- return TaskView.this.getItemInfo(mTask);
- }
-
- public TaskView getTaskView() {
- return TaskView.this;
- }
-
- public TaskViewIcon getIconView() {
- return mIconView;
- }
-
- public int getStagePosition() {
- return mStagePosition;
- }
-
- void setStagePosition(@SplitConfigurationOptions.StagePosition int stagePosition) {
- this.mStagePosition = stagePosition;
- }
-
- public int getA11yNodeId() {
- return mA11yNodeId;
- }
-
- public DigitalWellBeingToast getDigitalWellBeingToast() {
- return mDigitalWellBeingToast;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
new file mode 100644
index 0000000..1490fd0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -0,0 +1,1701 @@
+/*
+ * Copyright (C) 2017 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 android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.annotation.IdRes
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import android.graphics.Canvas
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.util.AttributeSet
+import android.util.FloatProperty
+import android.util.Log
+import android.view.Display
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.OnClickListener
+import android.view.ViewGroup
+import android.view.ViewStub
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.FrameLayout
+import android.widget.Toast
+import androidx.annotation.IntDef
+import androidx.annotation.VisibleForTesting
+import androidx.core.view.updateLayoutParams
+import com.android.app.animation.Interpolators
+import com.android.launcher3.Flags.enableCursorHoverStates
+import com.android.launcher3.Flags.enableFocusOutline
+import com.android.launcher3.Flags.enableGridOnlyOverview
+import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.Flags.privateSpaceRestrictAccessibilityDrag
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.LauncherState
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.config.FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.ItemInfoWithIcon
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.testing.TestLogging
+import com.android.launcher3.testing.shared.TestProtocol
+import com.android.launcher3.util.CancellableTask
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SafeCloseable
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.launcher3.util.TraceHelper
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.launcher3.util.ViewPool
+import com.android.launcher3.util.rects.set
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.RemoteAnimationTargets
+import com.android.quickstep.TaskAnimationManager
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.TaskUtils
+import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.orientation.RecentsPagedOrientationHandler
+import com.android.quickstep.task.thumbnail.TaskThumbnail
+import com.android.quickstep.task.thumbnail.TaskThumbnailView
+import com.android.quickstep.task.viewmodel.TaskViewData
+import com.android.quickstep.util.ActiveGestureErrorDetector
+import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.util.BorderAnimator
+import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator
+import com.android.quickstep.util.RecentsOrientedState
+import com.android.quickstep.util.TaskCornerRadius
+import com.android.quickstep.util.TaskRemovedDuringLaunchListener
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.QuickStepContract
+
+/** A task in the Recents view. */
+open class TaskView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+ focusBorderAnimator: BorderAnimator? = null,
+ hoverBorderAnimator: BorderAnimator? = null
+) : FrameLayout(context, attrs), ViewPool.Reusable {
+ /**
+ * Used in conjunction with [onTaskListVisibilityChanged], providing more granularity on which
+ * components of this task require an update
+ */
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS)
+ annotation class TaskDataChanges
+
+ /** Type of task view */
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(Type.SINGLE, Type.GROUPED, Type.DESKTOP)
+ annotation class Type {
+ companion object {
+ const val SINGLE = 1
+ const val GROUPED = 2
+ const val DESKTOP = 3
+ }
+ }
+
+ val taskViewData = TaskViewData()
+ val taskIds: IntArray
+ /** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
+ get() = taskContainers.map { it.task.key.id }.toIntArray()
+ val thumbnailViews: Array<TaskThumbnailViewDeprecated>
+ get() = taskContainers.map { it.thumbnailViewDeprecated }.toTypedArray()
+ val isGridTask: Boolean
+ /** Returns whether the task is part of overview grid and not being focused. */
+ get() = container.deviceProfile.isTablet && !isFocusedTask
+ val isRunningTask: Boolean
+ get() = this === recentsView?.runningTaskView
+ val isFocusedTask: Boolean
+ get() = this === recentsView?.focusedTaskView
+ val taskCornerRadius: Float
+ get() = currentFullscreenParams.cornerRadius
+ val recentsView: RecentsView<*, *>?
+ get() = parent as? RecentsView<*, *>
+ val pagedOrientationHandler: RecentsPagedOrientationHandler
+ get() = orientedState.orientationHandler
+
+ @get:Deprecated("Use [taskContainers] instead.")
+ val firstTask: Task
+ /** Returns the first task bound to this TaskView. */
+ get() = taskContainers[0].task
+ @get:Deprecated("Use [taskContainers] instead.")
+ val firstThumbnailViewDeprecated: TaskThumbnailViewDeprecated
+ /** Returns the first thumbnailView of the TaskView. */
+ get() = taskContainers[0].thumbnailViewDeprecated
+ @get:Deprecated("Use [taskContainers] instead.")
+ val firstItemInfo: ItemInfo
+ get() = taskContainers[0].itemInfo
+
+ private val currentFullscreenParams = FullscreenDrawParams(context)
+ protected val container: RecentsViewContainer =
+ RecentsViewContainer.containerFromContext(context)
+ protected val lastTouchDownPosition = PointF()
+
+ // Derived view properties
+ protected val persistentScale: Float
+ /**
+ * Returns multiplication of scale that is persistent (e.g. fullscreen and grid), and does
+ * not change according to a temporary state.
+ */
+ get() = Utilities.mapRange(gridProgress, nonGridScale, 1f)
+ protected val persistentTranslationX: Float
+ /**
+ * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does
+ * not change according to a temporary state (e.g. task offset).
+ */
+ get() =
+ (getNonGridTrans(nonGridTranslationX) +
+ getGridTrans(this.gridTranslationX) +
+ getNonGridTrans(nonGridPivotTranslationX))
+ protected val persistentTranslationY: Float
+ /**
+ * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does
+ * not change according to a temporary state (e.g. task offset).
+ */
+ get() = boxTranslationY + getGridTrans(gridTranslationY)
+ protected val primarySplitTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getPrimaryValue(
+ SPLIT_SELECT_TRANSLATION_X,
+ SPLIT_SELECT_TRANSLATION_Y
+ )
+ protected val secondarySplitTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getSecondaryValue(
+ SPLIT_SELECT_TRANSLATION_X,
+ SPLIT_SELECT_TRANSLATION_Y
+ )
+ protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+ protected val secondaryDismissTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getSecondaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+ protected val primaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getPrimaryValue(
+ TASK_OFFSET_TRANSLATION_X,
+ TASK_OFFSET_TRANSLATION_Y
+ )
+ protected val secondaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getSecondaryValue(
+ TASK_OFFSET_TRANSLATION_X,
+ TASK_OFFSET_TRANSLATION_Y
+ )
+ protected val taskResistanceTranslationProperty: FloatProperty<TaskView>
+ get() =
+ pagedOrientationHandler.getSecondaryValue(
+ TASK_RESISTANCE_TRANSLATION_X,
+ TASK_RESISTANCE_TRANSLATION_Y
+ )
+
+ private val tempCoordinates = FloatArray(2)
+ private val focusBorderAnimator: BorderAnimator?
+ private val hoverBorderAnimator: BorderAnimator?
+ private val rootViewDisplayId: Int
+ get() = rootView.display?.displayId ?: Display.DEFAULT_DISPLAY
+
+ /** Returns a list of all TaskContainers in the TaskView. */
+ lateinit var taskContainers: List<TaskContainer>
+ protected set
+ lateinit var orientedState: RecentsOrientedState
+
+ var taskViewId = -1
+ var isEndQuickSwitchCuj = false
+
+ // Various animation progress variables.
+ // progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
+ protected var fullscreenProgress = 0f
+ set(value) {
+ field = Utilities.boundToRange(value, 0f, 1f)
+ onFullscreenProgressChanged(field)
+ }
+ // gridProgress 0 = carousel; 1 = 2 row grid.
+ protected var gridProgress = 0f
+ set(value) {
+ field = value
+ onGridProgressChanged()
+ }
+ /**
+ * The modalness of this view is how it should be displayed when it is shown on its own in the
+ * modal state of overview. 0 being in context with other tasks, 1 being shown on its own.
+ */
+ protected var modalness = 0f
+ set(value) {
+ field = value
+ onModalnessUpdated(field)
+ }
+ protected var taskThumbnailSplashAlpha = 0f
+ set(value) {
+ field = value
+ applyThumbnailSplashAlpha()
+ }
+ protected var nonGridScale = 1f
+ set(value) {
+ field = value
+ applyScale()
+ }
+ private var dismissScale = 1f
+ set(value) {
+ field = value
+ applyScale()
+ }
+ private var dismissTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ private var dismissTranslationY = 0f
+ set(value) {
+ field = value
+ applyTranslationY()
+ }
+ private var taskOffsetTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ private var taskOffsetTranslationY = 0f
+ set(value) {
+ field = value
+ applyTranslationY()
+ }
+ private var taskResistanceTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ private var taskResistanceTranslationY = 0f
+ set(value) {
+ field = value
+ applyTranslationY()
+ }
+ // The following translation variables should only be used in the same orientation as Launcher.
+ private var boxTranslationY = 0f
+ set(value) {
+ field = value
+ applyTranslationY()
+ }
+ // The following grid translations scales with mGridProgress.
+ protected var gridTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ var gridTranslationY = 0f
+ protected set(value) {
+ field = value
+ applyTranslationY()
+ }
+ // The following grid translation is used to animate closing the gap between grid and clear all.
+ private var gridEndTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
+ // switch.
+ protected var nonGridTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ protected var nonGridPivotTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ // Used when in SplitScreenSelectState
+ private var splitSelectTranslationY = 0f
+ set(value) {
+ field = value
+ applyTranslationY()
+ }
+ private var splitSelectTranslationX = 0f
+ set(value) {
+ field = value
+ applyTranslationX()
+ }
+ protected var stableAlpha = 1f
+ set(value) {
+ field = value
+ alpha = stableAlpha
+ }
+ protected var shouldShowScreenshot = false
+ get() = !isRunningTask || field
+ /** Enable or disable showing border on hover and focus change */
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ var borderEnabled = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ // Set the animation correctly in case it misses the hover/focus event during state
+ // transition
+ hoverBorderAnimator?.setBorderVisibility(visible = field && isHovered, animated = true)
+ focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
+ }
+ protected var iconScaleAnimStartProgress = 0f
+ private var focusTransitionProgress = 1f
+
+ private var iconAndDimAnimator: ObjectAnimator? = null
+ // The current background requests to load the task thumbnail and icon
+ private val pendingThumbnailLoadRequests = mutableListOf<CancellableTask<*>>()
+ private val pendingIconLoadRequests = mutableListOf<CancellableTask<*>>()
+ private var isClickableAsLiveTile = true
+
+ init {
+ setOnClickListener { _ -> onClick() }
+ val keyboardFocusHighlightEnabled =
+ (ENABLE_KEYBOARD_QUICK_SWITCH.get() || enableFocusOutline())
+ val cursorHoverStatesEnabled = enableCursorHoverStates()
+ setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled)
+ context.obtainStyledAttributes(attrs, R.styleable.TaskView, defStyleAttr, defStyleRes).use {
+ this.focusBorderAnimator =
+ focusBorderAnimator
+ ?: if (keyboardFocusHighlightEnabled)
+ createSimpleBorderAnimator(
+ currentFullscreenParams.cornerRadius.toInt(),
+ context.resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_border_width
+ ),
+ { bounds: Rect -> getThumbnailBounds(bounds) },
+ this,
+ it.getColor(
+ R.styleable.TaskView_focusBorderColor,
+ BorderAnimator.DEFAULT_BORDER_COLOR
+ )
+ )
+ else null
+ this.hoverBorderAnimator =
+ hoverBorderAnimator
+ ?: if (cursorHoverStatesEnabled)
+ createSimpleBorderAnimator(
+ currentFullscreenParams.cornerRadius.toInt(),
+ context.resources.getDimensionPixelSize(
+ R.dimen.task_hover_border_width
+ ),
+ { bounds: Rect -> getThumbnailBounds(bounds) },
+ this,
+ it.getColor(
+ R.styleable.TaskView_hoverBorderColor,
+ BorderAnimator.DEFAULT_BORDER_COLOR
+ )
+ )
+ else null
+ }
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ public override fun onFocusChanged(
+ gainFocus: Boolean,
+ direction: Int,
+ previouslyFocusedRect: Rect?
+ ) {
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
+ if (borderEnabled) {
+ focusBorderAnimator?.setBorderVisibility(gainFocus, /* animated= */ true)
+ }
+ }
+
+ override fun onHoverEvent(event: MotionEvent): Boolean {
+ if (borderEnabled) {
+ when (event.action) {
+ MotionEvent.ACTION_HOVER_ENTER ->
+ hoverBorderAnimator?.setBorderVisibility(visible = true, animated = true)
+ MotionEvent.ACTION_HOVER_EXIT ->
+ hoverBorderAnimator?.setBorderVisibility(visible = false, animated = true)
+ else -> {}
+ }
+ }
+ return super.onHoverEvent(event)
+ }
+
+ // avoid triggering hover event on child elements which would cause HOVER_EXIT for this
+ // task view
+ override fun onInterceptHoverEvent(event: MotionEvent) =
+ if (enableCursorHoverStates()) true else super.onInterceptHoverEvent(event)
+
+ override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
+ val recentsView = recentsView ?: return false
+ val splitSelectStateController = recentsView.splitSelectController
+ // Disable taps for split selection animation unless we have a task not being selected
+ if (
+ splitSelectStateController.isSplitSelectActive &&
+ taskContainers.none { it.task.key.id != splitSelectStateController.initialTaskId }
+ ) {
+ return false
+ }
+ if (ev.action == MotionEvent.ACTION_DOWN) {
+ with(lastTouchDownPosition) {
+ x = ev.x
+ y = ev.y
+ }
+ }
+ return super.dispatchTouchEvent(ev)
+ }
+
+ override fun draw(canvas: Canvas) {
+ // Draw border first so any child views outside of the thumbnail bounds are drawn above it.
+ focusBorderAnimator?.drawBorder(canvas)
+ hoverBorderAnimator?.drawBorder(canvas)
+ super.draw(canvas)
+ }
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ val thumbnailTopMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+ if (container.deviceProfile.isTablet) {
+ pivotX = (if (layoutDirection == LAYOUT_DIRECTION_RTL) 0 else right - left).toFloat()
+ pivotY = thumbnailTopMargin.toFloat()
+ } else {
+ pivotX = (right - left) * 0.5f
+ pivotY = thumbnailTopMargin + (height - thumbnailTopMargin) * 0.5f
+ }
+ systemGestureExclusionRects =
+ SYSTEM_GESTURE_EXCLUSION_RECT.onEach {
+ it.right = width
+ it.bottom = height
+ }
+ }
+
+ override fun onRecycle() {
+ resetPersistentViewTransforms()
+ // Clear any references to the thumbnail (it will be re-read either from the cache or the
+ // system on next bind)
+ // TODO(b/334825222): Implement thumbnail/snapshot for the new [TaskThumbnailView].
+ if (enableRefactorTaskThumbnail()) {
+ notifyIsRunningTaskUpdated()
+ } else {
+ taskContainers.forEach { it.thumbnailViewDeprecated.setThumbnail(it.task, null) }
+ }
+ setOverlayEnabled(false)
+ onTaskListVisibilityChanged(false)
+ borderEnabled = false
+ }
+
+ // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
+ override fun hasOverlappingRendering() = false
+
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
+ super.onInitializeAccessibilityNodeInfo(info)
+ with(info) {
+ addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ R.string.accessibility_close,
+ context.getText(R.string.accessibility_close)
+ )
+ )
+ taskContainers.forEach {
+ TraceHelper.allowIpcs("TV.a11yInfo") {
+ TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
+ addAction(shortcut.createAccessibilityAction(context))
+ }
+ }
+ }
+ // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
+ if (taskContainers[0].digitalWellBeingToast?.hasLimit() == true) {
+ addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ R.string.accessibility_app_usage_settings,
+ context.getText(R.string.accessibility_app_usage_settings)
+ )
+ )
+ }
+ recentsView?.let {
+ collectionItemInfo =
+ AccessibilityNodeInfo.CollectionItemInfo.obtain(
+ 0,
+ 1,
+ it.taskViewCount - it.indexOfChild(this@TaskView) - 1,
+ 1,
+ false
+ )
+ }
+ }
+ }
+
+ override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
+ if (action == R.string.accessibility_close) {
+ recentsView?.dismissTask(this, true /*animateTaskView*/, true /*removeTask*/)
+ return true
+ }
+ if (action == R.string.accessibility_app_usage_settings) {
+ // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
+ taskContainers[0].digitalWellBeingToast?.openAppUsageSettings(this)
+ return true
+ }
+ taskContainers.forEach {
+ TaskOverlayFactory.getEnabledShortcuts(this, it).forEach { shortcut ->
+ if (shortcut.hasHandlerForAction(action)) {
+ shortcut.onClick(this)
+ return true
+ }
+ }
+ }
+ return super.performAccessibilityAction(action, arguments)
+ }
+
+ /** Updates this task view to the given {@param task}. */
+ open fun bind(
+ task: Task,
+ orientedState: RecentsOrientedState,
+ taskOverlayFactory: TaskOverlayFactory
+ ) {
+ cancelPendingLoadTasks()
+ taskContainers =
+ listOf(
+ createTaskContainer(
+ task,
+ R.id.snapshot,
+ R.id.icon,
+ R.id.show_windows,
+ STAGE_POSITION_UNDEFINED,
+ taskOverlayFactory
+ )
+ )
+ setOrientationState(orientedState)
+ }
+
+ protected fun createTaskContainer(
+ task: Task,
+ @IdRes thumbnailViewId: Int,
+ @IdRes iconViewId: Int,
+ @IdRes showWindowViewId: Int,
+ @StagePosition stagePosition: Int,
+ taskOverlayFactory: TaskOverlayFactory
+ ): TaskContainer {
+ val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = findViewById(thumbnailViewId)!!
+ val thumbnailView: TaskThumbnailView?
+ if (enableRefactorTaskThumbnail()) {
+ val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
+ thumbnailView =
+ TaskThumbnailView(context).apply {
+ layoutParams = thumbnailViewDeprecated.layoutParams
+ addView(this, indexOfSnapshotView)
+ }
+ thumbnailViewDeprecated.visibility = GONE
+ } else {
+ thumbnailView = null
+ }
+ val iconView = getOrInflateIconView(iconViewId)
+ return TaskContainer(
+ task,
+ thumbnailView,
+ thumbnailViewDeprecated,
+ iconView,
+ TransformingTouchDelegate(iconView.asView()),
+ stagePosition,
+ DigitalWellBeingToast(container, this),
+ findViewById(showWindowViewId)!!,
+ taskOverlayFactory
+ )
+ .apply {
+ if (enableRefactorTaskThumbnail()) {
+ thumbnailViewDeprecated.setTaskOverlay(overlay)
+ bindThumbnailView()
+ } else {
+ thumbnailViewDeprecated.bind(task, overlay)
+ }
+ }
+ }
+
+ protected fun getOrInflateIconView(@IdRes iconViewId: Int): TaskViewIcon {
+ val iconView = findViewById<View>(iconViewId)!!
+ return iconView as? TaskViewIcon
+ ?: (iconView as ViewStub)
+ .apply {
+ layoutResource =
+ if (enableOverviewIconMenu()) R.layout.icon_app_chip_view
+ else R.layout.icon_view
+ }
+ .inflate() as TaskViewIcon
+ }
+
+ protected fun isTaskContainersInitialized() = this::taskContainers.isInitialized
+
+ fun containsMultipleTasks() = taskContainers.size > 1
+
+ /**
+ * Returns the TaskContainer corresponding to a given taskId, or null if the TaskView does not
+ * contain a Task with that ID.
+ */
+ fun getTaskContainerById(taskId: Int) = taskContainers.firstOrNull { it.task.key.id == taskId }
+
+ /** Check if given `taskId` is tracked in this view */
+ fun containsTaskId(taskId: Int) = getTaskContainerById(taskId) != null
+
+ open fun setOrientationState(orientationState: RecentsOrientedState) {
+ this.orientedState = orientationState
+ taskContainers.forEach { it.iconView.setIconOrientation(orientationState, isGridTask) }
+ setThumbnailOrientation(orientationState)
+ }
+
+ protected open fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
+ taskContainers.forEach {
+ it.overlay.updateOrientationState(orientationState)
+ it.digitalWellBeingToast?.initialize(it.task)
+ }
+ }
+
+ /**
+ * Updates TaskView scaling and translation required to support variable width if enabled, while
+ * ensuring TaskView fits into screen in fullscreen.
+ */
+ fun updateTaskSize(
+ lastComputedTaskSize: Rect,
+ lastComputedGridTaskSize: Rect,
+ lastComputedCarouselTaskSize: Rect
+ ) {
+ val thumbnailPadding = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+ val taskWidth = lastComputedTaskSize.width()
+ val taskHeight = lastComputedTaskSize.height()
+ val nonGridScale: Float
+ val boxTranslationY: Float
+ val expectedWidth: Int
+ val expectedHeight: Int
+ if (container.deviceProfile.isTablet) {
+ val boxWidth: Int
+ val boxHeight: Int
+ if (isFocusedTask) {
+ // Task will be focused and should use focused task size. Use focusTaskRatio
+ // that is associated with the original orientation of the focused task.
+ boxWidth = taskWidth
+ boxHeight = taskHeight
+ } else {
+ // Otherwise task is in grid, and should use lastComputedGridTaskSize.
+ boxWidth = lastComputedGridTaskSize.width()
+ boxHeight = lastComputedGridTaskSize.height()
+ }
+
+ // Bound width/height to the box size.
+ expectedWidth = boxWidth
+ expectedHeight = boxHeight + thumbnailPadding
+
+ // Scale to to fit task Rect.
+ nonGridScale =
+ if (enableGridOnlyOverview()) {
+ lastComputedCarouselTaskSize.width() / taskWidth.toFloat()
+ } else {
+ taskWidth / boxWidth.toFloat()
+ }
+
+ // Align to top of task Rect.
+ boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f
+ } else {
+ nonGridScale = 1f
+ boxTranslationY = 0f
+ expectedWidth = if (enableOverviewIconMenu()) taskWidth else LayoutParams.MATCH_PARENT
+ expectedHeight =
+ if (enableOverviewIconMenu()) taskHeight + thumbnailPadding
+ else LayoutParams.MATCH_PARENT
+ }
+ this.nonGridScale = nonGridScale
+ this.boxTranslationY = boxTranslationY
+ updateLayoutParams<ViewGroup.LayoutParams> {
+ width = expectedWidth
+ height = expectedHeight
+ }
+ updateThumbnailSize()
+ }
+
+ protected open fun updateThumbnailSize() {
+ // TODO(b/271468547), we should default to setting translations only on the snapshot instead
+ // of a hybrid of both margins and translations
+ taskContainers[0].snapshotView.updateLayoutParams<LayoutParams> {
+ topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
+ }
+
+ /** Returns the thumbnail's bounds, optionally relative to the screen. */
+ @JvmOverloads
+ open fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean = false) {
+ bounds.setEmpty()
+ taskContainers.forEach {
+ val thumbnailBounds = Rect()
+ if (relativeToDragLayer) {
+ container.dragLayer.getDescendantRectRelativeToSelf(it.snapshotView, bounds)
+ } else {
+ thumbnailBounds.set(it.snapshotView)
+ }
+ bounds.union(thumbnailBounds)
+ }
+ }
+
+ /**
+ * See [TaskDataChanges]
+ *
+ * @param visible If this task view will be visible to the user in overview or hidden
+ */
+ fun onTaskListVisibilityChanged(visible: Boolean) {
+ onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL)
+ }
+
+ /**
+ * See [TaskDataChanges]
+ *
+ * @param visible If this task view will be visible to the user in overview or hidden
+ */
+ open fun onTaskListVisibilityChanged(visible: Boolean, @TaskDataChanges changes: Int) {
+ cancelPendingLoadTasks()
+ val recentsModel = RecentsModel.INSTANCE.get(context)
+ // These calls are no-ops if the data is already loaded, try and load the high
+ // resolution thumbnail if the state permits
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ if (!enableRefactorTaskThumbnail()) {
+ // TODO(b/334825222) add thumbnail state
+ taskContainers.forEach {
+ if (visible) {
+ recentsModel.thumbnailCache
+ .updateThumbnailInBackground(it.task) { thumbnailData ->
+ it.thumbnailViewDeprecated.setThumbnail(it.task, thumbnailData)
+ }
+ ?.also { request -> pendingThumbnailLoadRequests.add(request) }
+ } else {
+ it.thumbnailViewDeprecated.setThumbnail(null, null)
+ // Reset the task thumbnail reference as well (it will be fetched from the
+ // cache or reloaded next time we need it)
+ it.task.thumbnail = null
+ }
+ }
+ }
+ }
+ if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+ taskContainers.forEach {
+ if (visible) {
+ recentsModel.iconCache
+ .updateIconInBackground(it.task) { thumbnailData ->
+ setIcon(it.iconView, thumbnailData.icon)
+ if (enableOverviewIconMenu()) {
+ setText(it.iconView, thumbnailData.title)
+ }
+ it.digitalWellBeingToast?.initialize(thumbnailData)
+ }
+ ?.also { request -> pendingIconLoadRequests.add(request) }
+ } else {
+ setIcon(it.iconView, null)
+ if (enableOverviewIconMenu()) {
+ setText(it.iconView, null)
+ }
+ }
+ }
+ }
+ if (needsUpdate(changes, FLAG_UPDATE_CORNER_RADIUS)) {
+ currentFullscreenParams.updateCornerRadius(context)
+ }
+ }
+
+ protected open fun needsUpdate(@TaskDataChanges dataChange: Int, @TaskDataChanges flag: Int) =
+ (dataChange and flag) == flag
+
+ protected open fun cancelPendingLoadTasks() {
+ pendingThumbnailLoadRequests.forEach { it.cancel() }
+ pendingThumbnailLoadRequests.clear()
+ pendingIconLoadRequests.forEach { it.cancel() }
+ pendingIconLoadRequests.clear()
+ }
+
+ protected fun setIcon(iconView: TaskViewIcon, icon: Drawable?) {
+ with(iconView) {
+ if (icon != null) {
+ setDrawable(icon)
+ setOnClickListener {
+ if (!confirmSecondSplitSelectApp()) {
+ showTaskMenu(this)
+ }
+ }
+ setOnLongClickListener {
+ requestDisallowInterceptTouchEvent(true)
+ showTaskMenu(this)
+ }
+ } else {
+ setDrawable(null)
+ setOnClickListener(null)
+ setOnLongClickListener(null)
+ }
+ }
+ }
+
+ protected fun setText(iconView: TaskViewIcon, text: CharSequence?) {
+ iconView.setText(text)
+ }
+
+ open fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
+ if (enableRefactorTaskThumbnail()) {
+ // TODO(b/334825222) add thumbnail logic
+ return
+ }
+
+ taskContainers.forEach {
+ val thumbnailData = thumbnailDatas?.get(it.task.key.id)
+ if (thumbnailData != null) {
+ it.thumbnailViewDeprecated.setThumbnail(it.task, thumbnailData)
+ } else {
+ it.thumbnailViewDeprecated.refresh()
+ }
+ }
+ }
+
+ private fun onClick() {
+ if (confirmSecondSplitSelectApp()) {
+ Log.d("b/310064698", "${taskIds.contentToString()} - onClick - split select is active")
+ return
+ }
+ val callbackList =
+ launchTasks()?.apply {
+ add {
+ Log.d("b/310064698", "${taskIds.contentToString()} - onClick - launchCompleted")
+ }
+ }
+ Log.d("b/310064698", "${taskIds.contentToString()} - onClick - callbackList: $callbackList")
+ container.statsLogManager
+ .logger()
+ .withItemInfo(firstItemInfo)
+ .log(LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP)
+ }
+
+ /**
+ * Starts the task associated with this view and animates the startup.
+ *
+ * @return CompletionStage to indicate the animation completion or null if the launch failed.
+ */
+ open fun launchTaskAnimated(): RunnableList? {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN,
+ "startActivityFromRecentsAsync",
+ taskIds.contentToString()
+ )
+ val opts =
+ container.getActivityLaunchOptions(this, null).apply {
+ options.launchDisplayId = display?.displayId ?: Display.DEFAULT_DISPLAY
+ }
+ if (
+ ActivityManagerWrapper.getInstance()
+ .startActivityFromRecents(taskContainers[0].task.key, opts.options)
+ ) {
+ Log.d(
+ TAG,
+ "launchTaskAnimated - startActivityFromRecents: ${taskIds.contentToString()}"
+ )
+ ActiveGestureLog.INSTANCE.trackEvent(
+ ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED
+ )
+ val recentsView = recentsView ?: return null
+ if (recentsView.runningTaskViewId != -1) {
+ recentsView.onTaskLaunchedInLiveTileMode()
+
+ // Return a fresh callback in the live tile case, so that it's not accidentally
+ // triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
+ return RunnableList().also { recentsView.addSideTaskLaunchCallback(it) }
+ }
+ if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+ // If the recents transition is running (ie. in live tile mode), then the start
+ // of a new task will merge into the existing transition and it currently will
+ // not be run independently, so we need to rely on the onTaskAppeared() call
+ // for the new task to trigger the side launch callback to flush this runnable
+ // list (which is usually flushed when the app launch animation finishes)
+ recentsView.addSideTaskLaunchCallback(opts.onEndCallback)
+ }
+ return opts.onEndCallback
+ } else {
+ notifyTaskLaunchFailed()
+ return null
+ }
+ }
+
+ /** Starts the task associated with this view without any animation */
+ fun launchTask(callback: (launched: Boolean) -> Unit) {
+ launchTask(callback, isQuickSwitch = false)
+ }
+
+ /** Starts the task associated with this view without any animation */
+ open fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN,
+ "startActivityFromRecentsAsync",
+ taskIds.contentToString()
+ )
+ val firstContainer = taskContainers[0]
+ val failureListener = TaskRemovedDuringLaunchListener(context.applicationContext)
+ if (isQuickSwitch) {
+ // We only listen for failures to launch in quickswitch because the during this
+ // gesture launcher is in the background state, vs other launches which are in
+ // the actual overview state
+ failureListener.register(container, firstContainer.task.key.id) {
+ notifyTaskLaunchFailed()
+ recentsView?.let {
+ // Disable animations for now, as it is an edge case and the app usually
+ // covers launcher and also any state transition animation also gets
+ // clobbered by QuickstepTransitionManager.createWallpaperOpenAnimations
+ // when launcher shows again
+ it.startHome(false /* animated */)
+ // LauncherTaskbarUIController depends on the launcher state when
+ // checking whether to handle resume, but that can come in before
+ // startHome() changes the state, so force-refresh here to ensure the
+ // taskbar is updated
+ it.mSizeStrategy.taskbarController?.refreshResumedState()
+ }
+ }
+ }
+ // Indicate success once the system has indicated that the transition has started
+ val opts =
+ ActivityOptions.makeCustomTaskAnimation(
+ context,
+ 0,
+ 0,
+ Executors.MAIN_EXECUTOR.handler,
+ { callback(true) }
+ ) {
+ failureListener.onTransitionFinished()
+ }
+ .apply {
+ launchDisplayId = display?.displayId ?: Display.DEFAULT_DISPLAY
+ if (isQuickSwitch) {
+ setFreezeRecentTasksReordering()
+ }
+ // TODO(b/334826842) add splash functionality to new TTV
+ if (!enableRefactorTaskThumbnail()) {
+ disableStartingWindow =
+ firstContainer.thumbnailViewDeprecated.shouldShowSplashView()
+ }
+ }
+ Executors.UI_HELPER_EXECUTOR.execute {
+ if (
+ !ActivityManagerWrapper.getInstance()
+ .startActivityFromRecents(firstContainer.task.key, opts)
+ ) {
+ // If the call to start activity failed, then post the result immediately,
+ // otherwise, wait for the animation start callback from the activity options
+ // above
+ Executors.MAIN_EXECUTOR.post {
+ notifyTaskLaunchFailed()
+ callback(false)
+ }
+ }
+ Log.d(TAG, "launchTask - startActivityFromRecents: ${taskIds.contentToString()}")
+ }
+ }
+
+ /** Launch of the current task (both live and inactive tasks) with an animation. */
+ fun launchTasks(): RunnableList? {
+ val recentsView = recentsView ?: return null
+ val remoteTargetHandles = recentsView.mRemoteTargetHandles
+ if (!isRunningTask || remoteTargetHandles == null) {
+ return launchTaskAnimated()
+ }
+ if (!isClickableAsLiveTile) {
+ Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.")
+ return null
+ }
+ isClickableAsLiveTile = false
+ val targets =
+ if (remoteTargetHandles.size == 1) {
+ remoteTargetHandles[0].transformParams.targetSet
+ } else {
+ val apps =
+ remoteTargetHandles.flatMap { it.transformParams.targetSet.apps.asIterable() }
+ val wallpapers =
+ remoteTargetHandles.flatMap {
+ it.transformParams.targetSet.wallpapers.asIterable()
+ }
+ RemoteAnimationTargets(
+ apps.toTypedArray(),
+ wallpapers.toTypedArray(),
+ remoteTargetHandles[0].transformParams.targetSet.nonApps,
+ remoteTargetHandles[0].transformParams.targetSet.targetMode
+ )
+ }
+ if (targets == null) {
+ // If the recents animation is cancelled somehow between the parent if block and
+ // here, try to launch the task as a non live tile task.
+ val runnableList = launchTaskAnimated()
+ if (runnableList == null) {
+ Log.e(
+ TAG,
+ "Recents animation cancelled and cannot launch task as non-live tile" +
+ "; returning to home"
+ )
+ }
+ isClickableAsLiveTile = true
+ return runnableList
+ }
+ val runnableList = RunnableList()
+ with(AnimatorSet()) {
+ TaskViewUtils.composeRecentsLaunchAnimator(
+ this,
+ this@TaskView,
+ targets.apps,
+ targets.wallpapers,
+ targets.nonApps,
+ true /* launcherClosing */,
+ recentsView.stateManager,
+ recentsView,
+ recentsView.depthController
+ )
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animator: Animator) {
+ if (taskContainers.any { it.task.key.displayId != rootViewDisplayId }) {
+ launchTaskAnimated()
+ }
+ isClickableAsLiveTile = true
+ runEndCallback()
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ runEndCallback()
+ }
+
+ private fun runEndCallback() {
+ runnableList.executeAllAndDestroy()
+ }
+ }
+ )
+ start()
+ }
+ Log.d(TAG, "launchTasks - composeRecentsLaunchAnimator: ${taskIds.contentToString()}")
+ recentsView.onTaskLaunchedInLiveTileMode()
+ return runnableList
+ }
+
+ private fun notifyTaskLaunchFailed() {
+ val sb = StringBuilder("Failed to launch task \n")
+ taskContainers.forEach {
+ sb.append("(task=${it.task.key.baseIntent} userId=${it.task.key.userId})\n")
+ }
+ Log.w(TAG, sb.toString())
+ Toast.makeText(context, R.string.activity_not_available, Toast.LENGTH_SHORT).show()
+ }
+
+ fun initiateSplitSelect(splitPositionOption: SplitPositionOption) {
+ recentsView?.initiateSplitSelect(
+ this,
+ splitPositionOption.stagePosition,
+ SplitConfigurationOptions.getLogEventForPosition(splitPositionOption.stagePosition)
+ )
+ }
+
+ /**
+ * Returns `true` if user is already in split select mode and this tap was to choose the second
+ * app. `false` otherwise
+ */
+ protected open fun confirmSecondSplitSelectApp(): Boolean {
+ val index = getLastSelectedChildTaskIndex()
+ if (index >= taskContainers.size) {
+ return false
+ }
+ val container = taskContainers[index]
+ val recentsView = recentsView ?: return false
+ return recentsView.confirmSplitSelect(
+ this,
+ container.task,
+ container.iconView.drawable,
+ container.thumbnailViewDeprecated,
+ container.thumbnailViewDeprecated.thumbnail, /* intent */
+ null, /* user */
+ null,
+ container.itemInfo
+ )
+ }
+
+ /**
+ * Returns the task index of the last selected child task (0 or 1). If we contain multiple tasks
+ * and this TaskView is used as part of split selection, the selected child task index will be
+ * that of the remaining task.
+ */
+ protected open fun getLastSelectedChildTaskIndex() = 0
+
+ private fun showTaskMenu(iconView: TaskViewIcon): Boolean {
+ val recentsView = recentsView ?: return false
+ if (!recentsView.canLaunchFullscreenTask()) {
+ // Don't show menu when selecting second split screen app
+ return true
+ }
+ if (!container.deviceProfile.isTablet && !recentsView.isClearAllHidden) {
+ recentsView.snapToPage(recentsView.indexOfChild(this))
+ return false
+ }
+ val menuContainer = taskContainers.firstOrNull { it.iconView === iconView } ?: return false
+ container.statsLogManager
+ .logger()
+ .withItemInfo(menuContainer.itemInfo)
+ .log(LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS)
+ return showTaskMenuWithContainer(menuContainer)
+ }
+
+ private fun showTaskMenuWithContainer(menuContainer: TaskContainer): Boolean {
+ val recentsView = recentsView ?: return false
+ return if (enableOverviewIconMenu() && menuContainer.iconView is IconAppChipView) {
+ menuContainer.iconView.revealAnim(/* isRevealing= */ true)
+ TaskMenuView.showForTask(menuContainer) {
+ menuContainer.iconView.revealAnim(/* isRevealing= */ false)
+ }
+ } else if (container.deviceProfile.isTablet) {
+ val alignedOptionIndex =
+ if (
+ recentsView.isOnGridBottomRow(menuContainer.taskView) &&
+ container.deviceProfile.isLandscape
+ ) {
+ if (enableGridOnlyOverview()) {
+ // With no focused task, there is less available space below the tasks, so
+ // align the arrow to the third option in the menu.
+ 2
+ } else {
+ // Bottom row of landscape grid aligns arrow to second option to avoid
+ // clipping
+ 1
+ }
+ } else {
+ 0
+ }
+ TaskMenuViewWithArrow.showForTask(menuContainer, alignedOptionIndex)
+ } else {
+ TaskMenuView.showForTask(menuContainer)
+ }
+ }
+
+ /**
+ * Whether the taskview should take the touch event from parent. Events passed to children that
+ * might require special handling.
+ */
+ open fun offerTouchToChildren(event: MotionEvent): Boolean {
+ taskContainers.forEach {
+ if (event.action == MotionEvent.ACTION_DOWN) {
+ computeAndSetIconTouchDelegate(it.iconView, tempCoordinates, it.iconTouchDelegate)
+ if (it.iconTouchDelegate.onTouchEvent(event)) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ private fun computeAndSetIconTouchDelegate(
+ view: TaskViewIcon,
+ tempCenterCoordinates: FloatArray,
+ transformingTouchDelegate: TransformingTouchDelegate
+ ) {
+ val viewHalfWidth = view.width / 2f
+ val viewHalfHeight = view.height / 2f
+ Utilities.getDescendantCoordRelativeToAncestor(
+ view.asView(),
+ container.dragLayer,
+ tempCenterCoordinates.apply {
+ this[0] = viewHalfWidth
+ this[1] = viewHalfHeight
+ },
+ false
+ )
+ transformingTouchDelegate.setBounds(
+ (tempCenterCoordinates[0] - viewHalfWidth).toInt(),
+ (tempCenterCoordinates[1] - viewHalfHeight).toInt(),
+ (tempCenterCoordinates[0] + viewHalfWidth).toInt(),
+ (tempCenterCoordinates[1] + viewHalfHeight).toInt()
+ )
+ }
+
+ /** Sets up an on-click listener and the visibility for show_windows icon on top of the task. */
+ open fun setUpShowAllInstancesListener() {
+ taskContainers.forEach {
+ it.showWindowsView?.let { showWindowsView ->
+ updateFilterCallback(
+ showWindowsView,
+ getFilterUpdateCallback(it.task.key.packageName)
+ )
+ }
+ }
+ }
+
+ /**
+ * Returns a callback that updates the state of the filter and the recents overview
+ *
+ * @param taskPackageName package name of the task to filter by
+ */
+ private fun getFilterUpdateCallback(taskPackageName: String?) =
+ if (recentsView?.filterState?.shouldShowFilterUI(taskPackageName) == true)
+ OnClickListener { recentsView?.setAndApplyFilter(taskPackageName) }
+ else null
+
+ /**
+ * Sets the correct visibility and callback on the provided filterView based on whether the
+ * callback is null or not
+ */
+ private fun updateFilterCallback(filterView: View, callback: OnClickListener?) {
+ // Filtering changes alpha instead of the visibility since visibility
+ // can be altered separately through RecentsView#resetFromSplitSelectionState()
+ with(filterView) {
+ alpha = if (callback == null) 0f else 1f
+ setOnClickListener(callback)
+ }
+ }
+
+ protected open fun setIconsAndBannersFullscreenProgress(progress: Float) {
+ // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
+ // oversized and banner would look disproportionately large.
+ if (recentsView?.stateManager?.state == LauncherState.BACKGROUND_APP) {
+ return
+ }
+ setIconsAndBannersTransitionProgress(progress, invert = true)
+ }
+
+ /**
+ * Called to animate a smooth transition when going directly from an app into Overview (and vice
+ * versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
+ */
+ protected open fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
+ focusTransitionProgress = if (invert) 1 - progress else progress
+ getIconContentScale(invert).let { iconContentScale ->
+ taskContainers.forEach {
+ it.iconView.setContentAlpha(iconContentScale)
+ it.digitalWellBeingToast?.updateBannerOffset(1f - iconContentScale)
+ }
+ }
+ }
+
+ private fun getIconContentScale(invert: Boolean): Float {
+ val iconScalePercentage = SCALE_ICON_DURATION.toFloat() / DIM_ANIM_DURATION
+ val lowerClamp = if (invert) 1f - iconScalePercentage else 0f
+ val upperClamp = if (invert) 1f else iconScalePercentage
+ return Interpolators.clampToProgress(Interpolators.FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
+ .getInterpolation(focusTransitionProgress)
+ }
+
+ fun animateIconScaleAndDimIntoView() {
+ iconAndDimAnimator?.cancel()
+ iconAndDimAnimator =
+ ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1f).apply {
+ setCurrentFraction(iconScaleAnimStartProgress)
+ setDuration(DIM_ANIM_DURATION).interpolator = Interpolators.LINEAR
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ iconAndDimAnimator = null
+ }
+ }
+ )
+ start()
+ }
+ }
+
+ fun setIconScaleAndDim(iconScale: Float) {
+ iconAndDimAnimator?.cancel()
+ setIconsAndBannersTransitionProgress(iconScale, invert = false)
+ }
+
+ /** Set a color tint on the snapshot and supporting views. */
+ open fun setColorTint(amount: Float, tintColor: Int) {
+ taskContainers.forEach {
+ if (!enableRefactorTaskThumbnail()) {
+ // TODO(b/334832108) Add scrim to new TTV
+ it.thumbnailViewDeprecated.dimAlpha = amount
+ }
+ it.iconView.setIconColorTint(tintColor, amount)
+ it.digitalWellBeingToast?.setBannerColorTint(tintColor, amount)
+ }
+ }
+
+ /**
+ * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
+ * IconView is unaffected.
+ *
+ * @param taskId is only used when setting visibility to a non-[View.VISIBLE] value
+ */
+ open fun setThumbnailVisibility(visibility: Int, taskId: Int) {
+ taskContainers.forEach {
+ if (visibility == VISIBLE || it.task.key.id == taskId) {
+ it.snapshotView.visibility = visibility
+ it.digitalWellBeingToast?.setBannerVisibility(visibility)
+ it.showWindowsView?.visibility = visibility
+ it.overlay.setVisibility(visibility)
+ }
+ }
+ }
+
+ open fun setOverlayEnabled(overlayEnabled: Boolean) {
+ // TODO(b/335606129) Investigate the usage of [TaskOverlay] in the new TaskThumbnailView.
+ // and if it's still necessary we should support that in the new TTV class.
+ if (!enableRefactorTaskThumbnail()) {
+ taskContainers.forEach { it.thumbnailViewDeprecated.setOverlayEnabled(overlayEnabled) }
+ }
+ }
+
+ protected open fun refreshTaskThumbnailSplash() {
+ if (!enableRefactorTaskThumbnail()) {
+ // TODO(b/334826842) add splash functionality to new TTV
+ taskContainers.forEach { it.thumbnailViewDeprecated.refreshSplashView() }
+ }
+ }
+
+ protected fun getScrollAdjustment(gridEnabled: Boolean) =
+ if (gridEnabled) gridTranslationX else nonGridTranslationX
+
+ protected fun getOffsetAdjustment(gridEnabled: Boolean) = getScrollAdjustment(gridEnabled)
+
+ fun getSizeAdjustment(fullscreenEnabled: Boolean) = if (fullscreenEnabled) nonGridScale else 1f
+
+ private fun applyScale() {
+ val scale = persistentScale * dismissScale
+ scaleX = scale
+ scaleY = scale
+ if (enableRefactorTaskThumbnail()) {
+ taskViewData.scale.value = scale
+ }
+ updateSnapshotRadius()
+ }
+
+ protected open fun applyThumbnailSplashAlpha() {
+ if (!enableRefactorTaskThumbnail()) {
+ // TODO(b/334826842) add splash functionality to new TTV
+ taskContainers.forEach {
+ it.thumbnailViewDeprecated.setSplashAlpha(taskThumbnailSplashAlpha)
+ }
+ }
+ }
+
+ private fun applyTranslationX() {
+ translationX =
+ dismissTranslationX +
+ taskOffsetTranslationX +
+ taskResistanceTranslationX +
+ splitSelectTranslationX +
+ gridEndTranslationX +
+ persistentTranslationX
+ }
+
+ private fun applyTranslationY() {
+ translationY =
+ dismissTranslationY +
+ taskOffsetTranslationY +
+ taskResistanceTranslationY +
+ splitSelectTranslationY +
+ persistentTranslationY
+ }
+
+ private fun onGridProgressChanged() {
+ applyTranslationX()
+ applyTranslationY()
+ applyScale()
+ }
+
+ protected open fun onFullscreenProgressChanged(fullscreenProgress: Float) {
+ taskContainers.forEach {
+ it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
+ it.overlay.setFullscreenProgress(fullscreenProgress)
+ }
+ setIconsAndBannersFullscreenProgress(fullscreenProgress)
+ updateSnapshotRadius()
+ }
+
+ protected open fun updateSnapshotRadius() {
+ updateCurrentFullscreenParams()
+ taskContainers.forEach {
+ it.thumbnailViewDeprecated.setFullscreenParams(getThumbnailFullscreenParams())
+ it.overlay.setFullscreenParams(getThumbnailFullscreenParams())
+ }
+ }
+
+ protected open fun updateCurrentFullscreenParams() {
+ updateFullscreenParams(currentFullscreenParams)
+ }
+
+ protected fun updateFullscreenParams(fullscreenParams: FullscreenDrawParams) {
+ recentsView?.let { fullscreenParams.setProgress(fullscreenProgress, it.scaleX, scaleX) }
+ }
+
+ protected open fun getThumbnailFullscreenParams(): FullscreenDrawParams =
+ currentFullscreenParams
+
+ private fun onModalnessUpdated(modalness: Float) {
+ taskContainers.forEach {
+ it.iconView.setModalAlpha(1 - modalness)
+ it.digitalWellBeingToast?.updateBannerOffset(modalness)
+ }
+ }
+
+ /** Updates [TaskThumbnailView] to reflect the latest [Task] state (i.e., task isRunning). */
+ fun notifyIsRunningTaskUpdated() {
+ // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
+ // so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
+ taskContainers.forEach { it.bindThumbnailView() }
+ }
+
+ fun resetPersistentViewTransforms() {
+ nonGridTranslationX = 0f
+ gridTranslationX = 0f
+ gridTranslationY = 0f
+ boxTranslationY = 0f
+ nonGridPivotTranslationX = 0f
+ resetViewTransforms()
+ }
+
+ open fun resetViewTransforms() {
+ // fullscreenTranslation and accumulatedTranslation should not be reset, as
+ // resetViewTransforms is called during QuickSwitch scrolling.
+ dismissTranslationX = 0f
+ taskOffsetTranslationX = 0f
+ taskResistanceTranslationX = 0f
+ splitSelectTranslationX = 0f
+ gridEndTranslationX = 0f
+ dismissTranslationY = 0f
+ taskOffsetTranslationY = 0f
+ taskResistanceTranslationY = 0f
+ if (recentsView?.isSplitSelectionActive != true) {
+ splitSelectTranslationY = 0f
+ }
+ dismissScale = 1f
+ translationZ = 0f
+ alpha = stableAlpha
+ setIconScaleAndDim(1f)
+ setColorTint(0f, 0)
+ if (!enableRefactorTaskThumbnail()) {
+ // TODO(b/335399428) add split select functionality to new TTV
+ taskContainers.forEach { it.thumbnailViewDeprecated.resetViewTransforms() }
+ }
+ }
+
+ private fun getGridTrans(endTranslation: Float) =
+ Utilities.mapRange(gridProgress, 0f, endTranslation)
+
+ private fun getNonGridTrans(endTranslation: Float) =
+ endTranslation - getGridTrans(endTranslation)
+
+ /** We update and subsequently draw these in [fullscreenProgress]. */
+ open class FullscreenDrawParams(context: Context) : SafeCloseable {
+ var cornerRadius = 0f
+ private var windowCornerRadius = 0f
+ var currentDrawnCornerRadius = 0f
+
+ init {
+ updateCornerRadius(context)
+ }
+
+ /** Recomputes the start and end corner radius for the given Context. */
+ fun updateCornerRadius(context: Context) {
+ cornerRadius = computeTaskCornerRadius(context)
+ windowCornerRadius = computeWindowCornerRadius(context)
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ open fun computeTaskCornerRadius(context: Context): Float {
+ return TaskCornerRadius.get(context)
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ open fun computeWindowCornerRadius(context: Context): Float {
+ return QuickStepContract.getWindowCornerRadius(context)
+ }
+
+ /** Sets the progress in range [0, 1] */
+ fun setProgress(fullscreenProgress: Float, parentScale: Float, taskViewScale: Float) {
+ currentDrawnCornerRadius =
+ Utilities.mapRange(fullscreenProgress, cornerRadius, windowCornerRadius) /
+ parentScale /
+ taskViewScale
+ }
+
+ override fun close() {}
+ }
+
+ /** Holder for all Task dependent information. */
+ inner class TaskContainer(
+ val task: Task,
+ val thumbnailView: TaskThumbnailView?,
+ val thumbnailViewDeprecated: TaskThumbnailViewDeprecated,
+ val iconView: TaskViewIcon,
+ /**
+ * This technically can be a vanilla [android.view.TouchDelegate] class, however that class
+ * requires setting the touch bounds at construction, so we'd repeatedly be created many
+ * instances unnecessarily as scrolling occurs, whereas [TransformingTouchDelegate] allows
+ * touch delegated bounds only to be updated.
+ */
+ val iconTouchDelegate: TransformingTouchDelegate,
+ /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
+ @StagePosition val stagePosition: Int,
+ val digitalWellBeingToast: DigitalWellBeingToast?,
+ val showWindowsView: View?,
+ taskOverlayFactory: TaskOverlayFactory
+ ) {
+ val overlay: TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
+
+ @IdRes
+ val a11yNodeId: Int =
+ if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) R.id.split_bottomRight_appInfo
+ else R.id.split_topLeft_appInfo
+
+ val snapshotView: View
+ get() = thumbnailView ?: thumbnailViewDeprecated
+
+ /** Builds proto for logging */
+ val itemInfo: WorkspaceItemInfo
+ get() =
+ WorkspaceItemInfo().apply {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK
+ container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER
+ val componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key)
+ user = componentKey.user
+ intent = Intent().setComponent(componentKey.componentName)
+ title = task.title
+ recentsView?.let { screenId = it.indexOfChild(this@TaskView) }
+ if (privateSpaceRestrictAccessibilityDrag()) {
+ if (
+ UserCache.getInstance(context).getUserInfo(componentKey.user).isPrivate
+ ) {
+ runtimeStatusFlags =
+ runtimeStatusFlags or ItemInfoWithIcon.FLAG_NOT_PINNABLE
+ }
+ }
+ }
+
+ val taskView: TaskView
+ get() = this@TaskView
+
+ // TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
+ // so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
+ fun bindThumbnailView() {
+ thumbnailView?.viewModel?.bind(TaskThumbnail(task, isRunningTask))
+ }
+ }
+
+ companion object {
+ private const val TAG = "TaskView"
+ const val FLAG_UPDATE_ICON = 1
+ const val FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON shl 1
+ const val FLAG_UPDATE_CORNER_RADIUS = FLAG_UPDATE_THUMBNAIL shl 1
+ const val FLAG_UPDATE_ALL =
+ (FLAG_UPDATE_ICON or FLAG_UPDATE_THUMBNAIL or FLAG_UPDATE_CORNER_RADIUS)
+
+ /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
+ const val MAX_PAGE_SCRIM_ALPHA = 0.4f
+ const val SCALE_ICON_DURATION: Long = 120
+ private const val DIM_ANIM_DURATION: Long = 700
+ private val SYSTEM_GESTURE_EXCLUSION_RECT = listOf(Rect())
+
+ @JvmField
+ val FOCUS_TRANSITION: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("focusTransition") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.setIconsAndBannersTransitionProgress(v, false /* invert */)
+ }
+
+ override fun get(taskView: TaskView) = taskView.focusTransitionProgress
+ }
+ private val SPLIT_SELECT_TRANSLATION_X: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("splitSelectTranslationX") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.splitSelectTranslationX = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.splitSelectTranslationX
+ }
+ private val SPLIT_SELECT_TRANSLATION_Y: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("splitSelectTranslationY") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.splitSelectTranslationY = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.splitSelectTranslationY
+ }
+ private val DISMISS_TRANSLATION_X: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("dismissTranslationX") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.dismissTranslationX = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.dismissTranslationX
+ }
+ private val DISMISS_TRANSLATION_Y: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("dismissTranslationY") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.dismissTranslationY = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.dismissTranslationY
+ }
+ private val TASK_OFFSET_TRANSLATION_X: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("taskOffsetTranslationX") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.taskOffsetTranslationX = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.taskOffsetTranslationX
+ }
+ private val TASK_OFFSET_TRANSLATION_Y: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("taskOffsetTranslationY") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.taskOffsetTranslationY = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.taskOffsetTranslationY
+ }
+ private val TASK_RESISTANCE_TRANSLATION_X: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("taskResistanceTranslationX") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.taskResistanceTranslationX = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.taskResistanceTranslationX
+ }
+ private val TASK_RESISTANCE_TRANSLATION_Y: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("taskResistanceTranslationY") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.taskResistanceTranslationY = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.taskResistanceTranslationY
+ }
+ @JvmField
+ val GRID_END_TRANSLATION_X: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("gridEndTranslationX") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.gridEndTranslationX = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.gridEndTranslationX
+ }
+ @JvmField
+ val DISMISS_SCALE: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("dismissScale") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.dismissScale = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.dismissScale
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index d59aafb..36f2ccf 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -19,7 +19,7 @@
import android.content.ComponentName
import android.content.Intent
import android.platform.test.flag.junit.SetFlagsRule
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.launcher3.AbstractFloatingView
@@ -29,19 +29,26 @@
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay
import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.TaskThumbnailViewDeprecated
import com.android.quickstep.views.TaskView
+import com.android.quickstep.views.TaskViewIcon
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.Task.TaskKey
import com.android.window.flags.Flags
+import com.android.wm.shell.shared.DesktopModeStatus
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -58,8 +65,13 @@
private val taskView: TaskView = mock()
private val workspaceItemInfo: WorkspaceItemInfo = mock()
private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
+ private val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = mock()
+ private val iconView: TaskViewIcon = mock()
+ private val transformingTouchDelegate: TransformingTouchDelegate = mock()
private val factory: TaskShortcutFactory =
DesktopSystemShortcut.createFactory(abstractFloatingViewHelper)
+ private val overlayFactory: TaskOverlayFactory = mock()
+ private val overlay: TaskOverlay<*> = mock()
private lateinit var mockitoSession: StaticMockitoSession
@@ -70,8 +82,9 @@
.strictness(Strictness.LENIENT)
.spyStatic(DesktopModeStatus::class.java)
.startMocking()
- doReturn(true).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
+ ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ whenever(overlayFactory.createOverlay(any())).thenReturn(overlay)
}
@After
@@ -87,14 +100,7 @@
Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
isDockable = true
}
- val taskContainer =
- taskView.TaskContainer(
- task,
- null,
- null,
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- null
- )
+ val taskContainer = createTaskContainer(task)
val shortcuts = factory.getShortcuts(launcher, taskContainer)
assertThat(shortcuts).isNull()
@@ -103,20 +109,9 @@
@Test
fun createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported() {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
- val task =
- Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
- isDockable = true
- }
- val taskContainer =
- taskView.TaskContainer(
- task,
- null,
- null,
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- null
- )
+ val taskContainer = createTaskContainer(createTask())
val shortcuts = factory.getShortcuts(launcher, taskContainer)
assertThat(shortcuts).isNull()
@@ -125,21 +120,11 @@
@Test
fun createDesktopTaskShortcutFactory_desktopModeEnabled_DeviceNotSupported_OverrideEnabled() {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
- doReturn(false).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
+ ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
- val task =
- Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
- isDockable = true
- }
- val taskContainer =
- taskView.TaskContainer(
- task,
- null,
- null,
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- null
- )
+ val taskContainer = spy(createTaskContainer(createTask()))
+ doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
val shortcuts = factory.getShortcuts(launcher, taskContainer)
assertThat(shortcuts).isNotNull()
@@ -149,18 +134,8 @@
fun createDesktopTaskShortcutFactory_undockable() {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- val task =
- Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
- isDockable = false
- }
- val taskContainer =
- taskView.TaskContainer(
- task,
- null,
- null,
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- null
- )
+ val unDockableTask = createTask().apply { isDockable = false }
+ val taskContainer = createTaskContainer(unDockableTask)
val shortcuts = factory.getShortcuts(launcher, taskContainer)
assertThat(shortcuts).isNull()
@@ -170,28 +145,18 @@
fun desktopSystemShortcutClicked() {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- val task =
- Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
- isDockable = true
- }
- val taskContainer =
- taskView.TaskContainer(
- task,
- null,
- null,
- SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
- null
- )
+ val task = createTask()
+ val taskContainer = spy(createTaskContainer(task))
whenever(launcher.getOverviewPanel<LauncherRecentsView>()).thenReturn(recentsView)
whenever(launcher.statsLogManager).thenReturn(statsLogManager)
whenever(statsLogManager.logger()).thenReturn(statsLogger)
whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
- whenever(taskView.getItemInfo(task)).thenReturn(workspaceItemInfo)
whenever(recentsView.moveTaskToDesktop(any(), any())).thenAnswer {
val successCallback = it.getArgument<Runnable>(1)
successCallback.run()
}
+ doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
val shortcuts = factory.getShortcuts(launcher, taskContainer)
assertThat(shortcuts).hasSize(1)
@@ -208,4 +173,24 @@
verify(statsLogger).withItemInfo(workspaceItemInfo)
verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
}
+
+ private fun createTask(): Task {
+ return Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+ isDockable = true
+ }
+ }
+
+ private fun createTaskContainer(task: Task): TaskView.TaskContainer {
+ return taskView.TaskContainer(
+ task,
+ thumbnailView = null,
+ thumbnailViewDeprecated,
+ iconView,
+ transformingTouchDelegate,
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+ digitalWellBeingToast = null,
+ showWindowsView = null,
+ overlayFactory
+ )
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
index db06b6b..5d62a4c 100644
--- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -53,7 +53,7 @@
)
val expectedRadius = TaskCornerRadius.get(context)
- assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+ assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -67,7 +67,7 @@
)
val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
- assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+ assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -81,7 +81,7 @@
)
val expectedRadius = TaskCornerRadius.get(context)
- assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+ assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -95,7 +95,7 @@
)
val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
- assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
+ assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -117,7 +117,7 @@
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f
)
- assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1TaskRadius)
+ assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display1TaskRadius)
spyParams.updateCornerRadius(display2Context)
spyParams.setProgress(
@@ -125,7 +125,7 @@
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f
)
- assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2TaskRadius)
+ assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display2TaskRadius)
}
@Test
@@ -147,7 +147,7 @@
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f
)
- assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1WindowRadius)
+ assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display1WindowRadius)
spyParams.updateCornerRadius(display2Context)
spyParams.setProgress(
@@ -155,6 +155,6 @@
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f,
)
- assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2WindowRadius)
+ assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display2WindowRadius)
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
index 37ab131..07d8f61 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
@@ -86,9 +86,10 @@
final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
return getFromLauncher(launcher -> {
+ TaskView.TaskContainer taskContainer = task.getTaskContainers().get(0);
assertTrue("Latest task is not Calculator", CALCULATOR_PACKAGE.equals(
- task.getFirstTask().getTopComponent().getPackageName()));
- return task.getDigitalWellBeingToast();
+ taskContainer.getTask().getTopComponent().getPackageName()));
+ return taskContainer.getDigitalWellBeingToast();
});
}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index f29df61..250dc7b 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -87,7 +87,7 @@
@Before
fun setup() {
- whenever(mockTaskContainer.thumbnailView).thenReturn(mockThumbnailView)
+ whenever(mockTaskContainer.thumbnailViewDeprecated).thenReturn(mockThumbnailView)
whenever(mockThumbnailView.thumbnail).thenReturn(mockBitmap)
whenever(mockTaskContainer.iconView).thenReturn(mockIconView)
whenever(mockIconView.drawable).thenReturn(mockTaskViewDrawable)
@@ -180,7 +180,7 @@
whenever(mockTaskContainer.task).thenReturn(mockTask)
whenever(mockTaskContainer.iconView).thenReturn(mockIconView)
- whenever(mockTaskContainer.thumbnailView).thenReturn(mockThumbnailView)
+ whenever(mockTaskContainer.thumbnailViewDeprecated).thenReturn(mockThumbnailView)
whenever(mockTask.getKey()).thenReturn(mockTaskKey)
whenever(mockTaskKey.getId()).thenReturn(taskId)
whenever(mockSplitSelectStateController.initialTaskId).thenReturn(taskId)
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index f557fb6..99db8c6 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -50,9 +50,9 @@
android:id="@+id/action_btn"
android:layout_width="@dimen/x_icon_size"
android:layout_height="@dimen/x_icon_size"
+ android:scaleType="centerInside"
android:layout_gravity="center"
android:contentDescription="@string/accessibility_close"
- android:padding="@dimen/x_icon_padding"
android:background="@android:color/transparent"
android:src="@drawable/ic_remove_no_shadow" />
</FrameLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 74fadda..b4f8a47 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -167,7 +167,6 @@
<!-- (x) icon button inside work edu card -->
<dimen name="rounded_button_width">24dp</dimen>
<dimen name="x_icon_size">16dp</dimen>
- <dimen name="x_icon_padding">4dp</dimen>
<!-- rounded button shown inside card views, and snack bars -->
<dimen name="padded_rounded_button_height">48dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f114b1e..02e84e0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -340,7 +340,7 @@
<!-- Title for an app whose download has been started. -->
<string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
<!-- Title for an app which is archived. -->
- <string name="app_archived_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> is archived. Tap to download.</string>
+ <string name="app_archived_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> is archived. Tap to download and restore.</string>
<!-- Title shown on the alert dialog prompting the user to update the application in market
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index cf86528..175d6ec 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch;
+import static com.android.launcher3.util.UserIconInfo.TYPE_CLONED;
+import static com.android.launcher3.util.UserIconInfo.TYPE_WORK;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -34,6 +36,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
@@ -56,6 +59,7 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Partner;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.UserIconInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
import org.xmlpull.v1.XmlPullParser;
@@ -63,6 +67,7 @@
import java.io.IOException;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
@@ -125,34 +130,38 @@
private static final String TAG_INCLUDE = "include";
public static final String TAG_WORKSPACE = "workspace";
private static final String TAG_APP_ICON = "appicon";
- private static final String TAG_AUTO_INSTALL = "autoinstall";
- private static final String TAG_FOLDER = "folder";
- private static final String TAG_APPWIDGET = "appwidget";
+ public static final String TAG_AUTO_INSTALL = "autoinstall";
+ public static final String TAG_FOLDER = "folder";
+ public static final String TAG_APPWIDGET = "appwidget";
protected static final String TAG_SEARCH_WIDGET = "searchwidget";
- private static final String TAG_SHORTCUT = "shortcut";
+ public static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_EXTRA = "extra";
- private static final String ATTR_CONTAINER = "container";
- private static final String ATTR_RANK = "rank";
+ public static final String ATTR_CONTAINER = "container";
+ public static final String ATTR_RANK = "rank";
- private static final String ATTR_PACKAGE_NAME = "packageName";
- private static final String ATTR_CLASS_NAME = "className";
- private static final String ATTR_TITLE = "title";
- private static final String ATTR_TITLE_TEXT = "titleText";
- private static final String ATTR_SCREEN = "screen";
- private static final String ATTR_SHORTCUT_ID = "shortcutId";
+ public static final String ATTR_PACKAGE_NAME = "packageName";
+ public static final String ATTR_CLASS_NAME = "className";
+ public static final String ATTR_TITLE = "title";
+ public static final String ATTR_TITLE_TEXT = "titleText";
+ public static final String ATTR_SCREEN = "screen";
+ public static final String ATTR_SHORTCUT_ID = "shortcutId";
// x and y can be specified as negative integers, in which case -1 represents the
// last row / column, -2 represents the second last, and so on.
- private static final String ATTR_X = "x";
- private static final String ATTR_Y = "y";
+ public static final String ATTR_X = "x";
+ public static final String ATTR_Y = "y";
- private static final String ATTR_SPAN_X = "spanX";
- private static final String ATTR_SPAN_Y = "spanY";
+ public static final String ATTR_SPAN_X = "spanX";
+ public static final String ATTR_SPAN_Y = "spanY";
// Attrs for "Include"
private static final String ATTR_WORKSPACE = "workspace";
+ public static final String ATTR_USER_TYPE = "userType";
+ public static final String USER_TYPE_WORK = "work";
+ public static final String USER_TYPE_CLONED = "cloned";
+
// Style attrs -- "Extra"
private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE = "value";
@@ -168,6 +177,8 @@
protected final SourceResources mSourceRes;
protected final Supplier<XmlPullParser> mInitialLayoutSupplier;
+ private final Map<String, Long> mUserTypeToSerial;
+
private final InvariantDeviceProfile mIdp;
private final int mRowCount;
private final int mColumnCount;
@@ -204,15 +215,25 @@
mRowCount = mIdp.numRows;
mColumnCount = mIdp.numColumns;
mActivityOverride = ApiWrapper.INSTANCE.get(context).getActivityOverrides();
+
+ mUserTypeToSerial = new HashMap<>();
+ UserCache cache = UserCache.getInstance(context);
+ for (UserHandle user : cache.getUserProfiles()) {
+ UserIconInfo uii = cache.getUserInfo(user);
+ switch (uii.type) {
+ case TYPE_WORK -> mUserTypeToSerial.put(USER_TYPE_WORK, uii.userSerial);
+ case TYPE_CLONED -> mUserTypeToSerial.put(USER_TYPE_CLONED, uii.userSerial);
+ }
+ }
}
/**
* Loads the layout in the db and returns the number of entries added on the desktop.
*/
- public int loadLayout(SQLiteDatabase db, IntArray screenIds) {
+ public int loadLayout(SQLiteDatabase db) {
mDb = db;
try {
- return parseLayout(mInitialLayoutSupplier.get(), screenIds);
+ return parseLayout(mInitialLayoutSupplier.get());
} catch (Exception e) {
Log.e(TAG, "Error parsing layout: ", e);
return -1;
@@ -222,7 +243,7 @@
/**
* Parses the layout and returns the number of elements added on the homescreen.
*/
- protected int parseLayout(XmlPullParser parser, IntArray screenIds)
+ protected int parseLayout(XmlPullParser parser)
throws XmlPullParserException, IOException {
beginDocument(parser, mRootTag);
final int depth = parser.getDepth();
@@ -235,7 +256,7 @@
if (type != XmlPullParser.START_TAG) {
continue;
}
- count += parseAndAddNode(parser, tagParserMap, screenIds);
+ count += parseAndAddNode(parser, tagParserMap);
}
return count;
}
@@ -259,14 +280,14 @@
* Parses the current node and returns the number of elements added.
*/
protected int parseAndAddNode(
- XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
+ XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap)
throws XmlPullParserException, IOException {
if (TAG_INCLUDE.equals(parser.getName())) {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
if (resId != 0) {
// recursively load some more favorites, why not?
- return parseLayout(mSourceRes.getXml(resId), screenIds);
+ return parseLayout(mSourceRes.getXml(resId));
} else {
return 0;
}
@@ -284,22 +305,17 @@
convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
mValues.put(Favorites.CELLY,
convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));
+ Long profileId = mUserTypeToSerial.get(getAttributeValue(parser, ATTR_USER_TYPE));
+ if (profileId != null) {
+ mValues.put(Favorites.PROFILE_ID, profileId);
+ }
TagParser tagParser = tagParserMap.get(parser.getName());
if (tagParser == null) {
if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
return 0;
}
- int newElementId = tagParser.parseAndAdd(parser);
- if (newElementId >= 0) {
- // Keep track of the set of screens which need to be added to the db.
- if (!screenIds.contains(screenId) &&
- container == Favorites.CONTAINER_DESKTOP) {
- screenIds.add(screenId);
- }
- return 1;
- }
- return 0;
+ return tagParser.parseAndAdd(parser) >= 0 ? 1 : 0;
}
protected int addShortcut(String title, Intent intent, int type) {
@@ -311,10 +327,11 @@
mValues.put(Favorites.SPANY, 1);
mValues.put(Favorites._ID, id);
- if (type == ITEM_TYPE_APPLICATION) {
- ComponentName cn = intent.getComponent();
- if (cn != null && mActivityOverride.containsKey(cn.getPackageName())) {
- LauncherActivityInfo replacementInfo = mActivityOverride.get(cn.getPackageName());
+ ComponentName cn = intent.getComponent();
+ if (cn != null && type == ITEM_TYPE_APPLICATION
+ && !mValues.containsKey(Favorites.PROFILE_ID)) {
+ LauncherActivityInfo replacementInfo = mActivityOverride.get(cn.getPackageName());
+ if (replacementInfo != null) {
mValues.put(Favorites.PROFILE_ID, UserCache.INSTANCE.get(mContext)
.getSerialNumberForUser(replacementInfo.getUser()));
mValues.put(Favorites.INTENT, AppInfo.makeLaunchIntent(replacementInfo).toUri(0));
@@ -420,11 +437,7 @@
}
mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON);
- final Intent intent = new Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setComponent(new ComponentName(packageName, className))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ Intent intent = AppInfo.makeLaunchIntent(new ComponentName(packageName, className));
return addShortcut(mContext.getString(R.string.package_state_unknown), intent,
ITEM_TYPE_APPLICATION);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e3da389..47a7115 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -19,6 +19,7 @@
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
+import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.icons.cache.BaseIconCache.EMPTY_CLASS_NAME;
import static com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE;
@@ -276,6 +277,9 @@
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
}
+ if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ LauncherPrefs.get(mApp.getContext()).put(WORK_EDU_STEP, 0);
+ }
}
/**
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 3bdd863..72a3c53 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -83,7 +83,7 @@
public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(4);
// Flag to inticate that all popups should be closed when this state is enabled.
public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(5);
- public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(6);
+ public static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(6);
// Flag indicating that hotseat and its contents are not accessible.
public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(7);
@@ -158,14 +158,14 @@
/**
* True if the state has overview panel visible.
*/
- public final boolean overviewUi;
+ public final boolean isRecentsViewVisible;
private final int mFlags;
public LauncherState(int id, int statsLogOrdinal, int flags) {
this.statsLogOrdinal = statsLogOrdinal;
this.mFlags = flags;
- this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
+ this.isRecentsViewVisible = (flags & FLAG_RECENTS_VIEW_VISIBLE) != 0;
this.ordinal = id;
sAllStates[id] = this;
}
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 0792641..56a7fef 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.Flags.enableExpandingPauseWorkButton;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
-import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.WORK;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
@@ -587,12 +586,6 @@
return;
}
- if (!FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) {
- RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
- getSearchRecyclerView().removeItemDecoration(decoration);
- getSearchRecyclerView().addItemDecoration(decoration);
- }
-
// replaceAppsRVcontainer() needs to use both mUsingTabs value to remove the old view AND
// showTabs value to create new view. Hence the mUsingTabs new value assignment MUST happen
// after this call.
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index 64fd237..4a8c96b 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -19,8 +19,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.recyclerview.widget.RecyclerView;
-
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.model.data.ItemInfo;
@@ -30,13 +28,10 @@
* Provides views for local search results.
*/
public class DefaultSearchAdapterProvider extends SearchAdapterProvider<ActivityContext> {
-
- private final RecyclerView.ItemDecoration mDecoration;
private View mHighlightedView;
public DefaultSearchAdapterProvider(ActivityContext launcher) {
super(launcher);
- mDecoration = new RecyclerView.ItemDecoration() { };
}
@Override
@@ -74,11 +69,6 @@
}
@Override
- public RecyclerView.ItemDecoration getDecorator() {
- return mDecoration;
- }
-
- @Override
public void clearHighlightedItem() {
mHighlightedView = null;
}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index 15756f5..82c9c90 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -20,8 +20,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.recyclerview.widget.RecyclerView;
-
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.views.ActivityContext;
@@ -50,11 +48,6 @@
public abstract View getHighlightedItem();
/**
- * Returns the item decorator.
- */
- public abstract RecyclerView.ItemDecoration getDecorator();
-
- /**
* Clear the highlighted view.
*/
public abstract void clearHighlightedItem();
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index e1a7d66..4b908bf 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -61,15 +61,6 @@
* and set a default value for the flag. This will be the default value on Debug builds.
* <p>
*/
- // TODO(Block 1): Clean up flags
- public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag(
- 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", ENABLED,
- "Enable option to replace decorator-based search result backgrounds with drawables");
-
- public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag(
- 270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", ENABLED,
- "Enable option to launch search results using the new view container transitions");
-
// TODO(Block 2): Clean up flags
public static final BooleanFlag ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH = getDebugFlag(270395073,
"ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", DISABLED,
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 25eeacb..861631d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -781,7 +781,19 @@
LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN(1727),
@UiEvent(doc = "Private space unlock animation finished")
- LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END(1728)
+ LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END(1728),
+
+ @UiEvent(doc = "User rotates whilst in Overview / RecentsView")
+ LAUNCHER_OVERVIEW_ORIENTATION_CHANGED(1762),
+
+ @UiEvent(doc = "User launches Overview from 3 button navigation")
+ LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON(1763),
+
+ @UiEvent(doc = "User launches Overview from alt+tab keyboard quick switch")
+ LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH(1764),
+
+ @UiEvent(doc = "User launches Overview from meta+tab keyboard shortcut")
+ LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT(1765),
// ADD MORE
;
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index 132b606..8368256 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -505,7 +505,7 @@
public int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
// TODO: Use multiple loaders with fall-back and transaction.
- int count = loader.loadLayout(db, new IntArray());
+ int count = loader.loadLayout(db);
// Ensure that the max ids are initialized
mMaxItemId = initializeMaxItemId(db);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 102d28a..0d40a24 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -171,7 +171,7 @@
mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
mUserManager = mApp.getContext().getSystemService(UserManager.class);
mUserCache = UserCache.INSTANCE.get(mApp.getContext());
- mPmHelper = new PackageManagerHelper(mApp.getContext());
+ mPmHelper = PackageManagerHelper.INSTANCE.get(mApp.getContext());
mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
mIconCache = mApp.getIconCache();
mUserManagerState = userManagerState;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 29d2269..079987b 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -244,7 +244,16 @@
.query(ShortcutRequest.PINNED);
if (shortcut.isEmpty()) {
isTargetValid = false;
+ if (DEBUG) {
+ Log.d(TAG, "Pinned Shortcut not found for updated"
+ + " package=" + si.getTargetPackage());
+ }
} else {
+ if (DEBUG) {
+ Log.d(TAG, "Found pinned shortcut for updated"
+ + " package=" + si.getTargetPackage()
+ + ", isTargetValid=" + isTargetValid);
+ }
si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
@@ -252,6 +261,7 @@
isTargetValid = context.getSystemService(LauncherApps.class)
.isActivityEnabled(cn, mUser);
}
+
if (!isTargetValid && (si.hasStatusFlag(
FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)
|| si.isArchived())) {
@@ -260,20 +270,24 @@
} else if (si.hasPromiseIconUi()) {
removedShortcuts.add(si.id);
if (DEBUG) {
- Log.d(TAG, "Removing restored shortcut promise icon"
+ FileLog.w(TAG, "Removing restored shortcut promise icon"
+ " that no longer points to valid component."
+ " id=" + si.id
- + ", package=" + si.getTargetPackage());
+ + ", package=" + si.getTargetPackage()
+ + ", status=" + si.status
+ + ", isArchived=" + si.isArchived());
}
return;
}
} else if (!isTargetValid) {
removedShortcuts.add(si.id);
- FileLog.e(TAG, "Removing shortcut that no longer points to"
- + " valid component."
- + " id=" + si.id
- + " package=" + si.getTargetPackage()
- + " status=" + si.status);
+ if (DEBUG) {
+ FileLog.w(TAG, "Removing shortcut that no longer points to"
+ + " valid component."
+ + " id=" + si.id
+ + " package=" + si.getTargetPackage()
+ + " status=" + si.status);
+ }
return;
} else {
si.status = WorkspaceItemInfo.DEFAULT;
diff --git a/src/com/android/launcher3/model/UserManagerState.java b/src/com/android/launcher3/model/UserManagerState.java
index 720f08e..ed32430 100644
--- a/src/com/android/launcher3/model/UserManagerState.java
+++ b/src/com/android/launcher3/model/UserManagerState.java
@@ -17,6 +17,7 @@
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
@@ -27,6 +28,8 @@
*/
public class UserManagerState {
+ private static final String TAG = "UserManagerState";
+
public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
private final LongSparseArray<Boolean> mQuietUsersSerialNoMap = new LongSparseArray<>();
@@ -39,6 +42,13 @@
for (UserHandle user : userManager.getUserProfiles()) {
long serialNo = userCache.getSerialNumberForUser(user);
boolean isUserQuiet = userManager.isQuietModeEnabled(user);
+ // Mapping different UserHandles to the same serialNo in allUsers could lead to losing
+ // UserHandle and cause a series of problems, such as incorrectly marking app as
+ // disabled and deleting app icons from workspace.
+ if (allUsers.get(serialNo) != null) {
+ Log.w(TAG, String.format("Override allUsers[%d]=%s with %s",
+ serialNo, allUsers.get(serialNo), user));
+ }
allUsers.put(serialNo, user);
mQuietUsersHashCodeMap.put(user.hashCode(), isUserQuiet);
mQuietUsersSerialNoMap.put(serialNo, isUserQuiet);
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 2c533ac..40e3813 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.NonNull;
@@ -186,9 +187,12 @@
if (shortcutInfo.isEnabled()) {
runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
} else {
+ Log.w(TAG, "updateFromDeepShortcutInfo: Updated shortcut has been disabled. "
+ + " package=" + shortcutInfo.getPackage()
+ + " disabledReason=" + shortcutInfo.getDisabledReason());
runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
}
- disabledMessage = shortcutInfo.getDisabledMessage();
+
if (shortcutInfo.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
runtimeStatusFlags |= FLAG_DISABLED_VERSION_LOWER;
} else {
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index b7b557d..ed25186 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -101,6 +101,7 @@
mUserChangeReceiver.register(mContext,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+ Intent.ACTION_MANAGED_PROFILE_REMOVED,
ACTION_PROFILE_ADDED,
ACTION_PROFILE_REMOVED,
ACTION_PROFILE_UNLOCKED,
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 50f98f2..3817563 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -411,17 +411,29 @@
mLauncher.getStatsLogManager().logger()
.withSrcState(mStartState.statsLogOrdinal)
.withDstState(targetState.statsLogOrdinal)
- .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
- .setWorkspace(
- LauncherAtom.WorkspaceContainer.newBuilder()
- .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
- .build())
+ .withContainerInfo(getContainerInfo(targetState))
.log(StatsLogManager.getLauncherAtomEvent(mStartState.statsLogOrdinal,
targetState.statsLogOrdinal, mToState.ordinal > mFromState.ordinal
? LAUNCHER_UNKNOWN_SWIPEUP
: LAUNCHER_UNKNOWN_SWIPEDOWN));
}
+ private LauncherAtom.ContainerInfo getContainerInfo(LauncherState targetState) {
+ if (targetState.isRecentsViewVisible) {
+ return LauncherAtom.ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(
+ LauncherAtom.TaskSwitcherContainer.getDefaultInstance()
+ )
+ .build();
+ }
+
+ return LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
+ .build();
+ }
+
protected void clearState() {
cancelAnimationControllers();
mGoingBetweenStates = true;
diff --git a/src/com/android/launcher3/util/LauncherLayoutBuilder.kt b/src/com/android/launcher3/util/LauncherLayoutBuilder.kt
new file mode 100644
index 0000000..ecc9953
--- /dev/null
+++ b/src/com/android/launcher3/util/LauncherLayoutBuilder.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.util.Xml
+import com.android.launcher3.AutoInstallsLayout.ATTR_CLASS_NAME
+import com.android.launcher3.AutoInstallsLayout.ATTR_CONTAINER
+import com.android.launcher3.AutoInstallsLayout.ATTR_PACKAGE_NAME
+import com.android.launcher3.AutoInstallsLayout.ATTR_RANK
+import com.android.launcher3.AutoInstallsLayout.ATTR_SCREEN
+import com.android.launcher3.AutoInstallsLayout.ATTR_SHORTCUT_ID
+import com.android.launcher3.AutoInstallsLayout.ATTR_SPAN_X
+import com.android.launcher3.AutoInstallsLayout.ATTR_SPAN_Y
+import com.android.launcher3.AutoInstallsLayout.ATTR_TITLE
+import com.android.launcher3.AutoInstallsLayout.ATTR_TITLE_TEXT
+import com.android.launcher3.AutoInstallsLayout.ATTR_USER_TYPE
+import com.android.launcher3.AutoInstallsLayout.ATTR_X
+import com.android.launcher3.AutoInstallsLayout.ATTR_Y
+import com.android.launcher3.AutoInstallsLayout.TAG_APPWIDGET
+import com.android.launcher3.AutoInstallsLayout.TAG_AUTO_INSTALL
+import com.android.launcher3.AutoInstallsLayout.TAG_FOLDER
+import com.android.launcher3.AutoInstallsLayout.TAG_SHORTCUT
+import com.android.launcher3.AutoInstallsLayout.TAG_WORKSPACE
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.containerToString
+import java.io.IOException
+import java.io.StringWriter
+import java.io.Writer
+import org.xmlpull.v1.XmlSerializer
+
+/** Helper class to build xml for Launcher Layout */
+class LauncherLayoutBuilder {
+ private val nodes = ArrayList<Node>()
+
+ fun atHotseat(rank: Int) =
+ ItemTarget(
+ mapOf(
+ ATTR_CONTAINER to containerToString(CONTAINER_HOTSEAT),
+ ATTR_RANK to rank.toString()
+ )
+ )
+
+ fun atWorkspace(x: Int, y: Int, screen: Int) =
+ ItemTarget(
+ mapOf(
+ ATTR_CONTAINER to containerToString(CONTAINER_DESKTOP),
+ ATTR_X to x.toString(),
+ ATTR_Y to y.toString(),
+ ATTR_SCREEN to screen.toString()
+ )
+ )
+
+ @Throws(IOException::class) fun build() = StringWriter().apply { build(this) }.toString()
+
+ @Throws(IOException::class)
+ fun build(writer: Writer) {
+ Xml.newSerializer().apply {
+ setOutput(writer)
+ setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
+ startDocument("UTF-8", true)
+ startTag(null, TAG_WORKSPACE)
+ writeNodes(nodes)
+ endTag(null, TAG_WORKSPACE)
+ endDocument()
+ flush()
+ }
+ }
+
+ open inner class ItemTarget(private val baseValues: Map<String, String>) {
+ @JvmOverloads
+ fun putApp(packageName: String, className: String?, userType: String? = null) =
+ addItem(
+ TAG_AUTO_INSTALL,
+ userType,
+ mapOf(
+ ATTR_PACKAGE_NAME to packageName,
+ ATTR_CLASS_NAME to (className ?: packageName)
+ )
+ )
+
+ @JvmOverloads
+ fun putShortcut(packageName: String, shortcutId: String, userType: String? = null) =
+ addItem(
+ TAG_SHORTCUT,
+ userType,
+ mapOf(ATTR_PACKAGE_NAME to packageName, ATTR_SHORTCUT_ID to shortcutId)
+ )
+
+ @JvmOverloads
+ fun putWidget(
+ packageName: String,
+ className: String,
+ spanX: Int,
+ spanY: Int,
+ userType: String? = null
+ ) =
+ addItem(
+ TAG_APPWIDGET,
+ userType,
+ mapOf(
+ ATTR_PACKAGE_NAME to packageName,
+ ATTR_CLASS_NAME to className,
+ ATTR_SPAN_X to spanX.toString(),
+ ATTR_SPAN_Y to spanY.toString()
+ )
+ )
+
+ fun putFolder(titleResId: Int) = putFolder(ATTR_TITLE, titleResId.toString())
+
+ fun putFolder(title: String?) = putFolder(ATTR_TITLE_TEXT, title)
+
+ protected open fun addItem(
+ tag: String,
+ userType: String?,
+ props: Map<String, String>,
+ children: List<Node>? = null
+ ): LauncherLayoutBuilder {
+ nodes.add(
+ Node(
+ tag,
+ HashMap(baseValues).apply {
+ putAll(props)
+ userType?.let { put(ATTR_USER_TYPE, it) }
+ },
+ children
+ )
+ )
+ return this@LauncherLayoutBuilder
+ }
+
+ protected open fun putFolder(titleKey: String, titleValue: String?): FolderBuilder {
+ val folderBuilder = FolderBuilder()
+ addItem(TAG_FOLDER, null, mapOf(titleKey to (titleValue ?: "")), folderBuilder.children)
+ return folderBuilder
+ }
+ }
+
+ inner class FolderBuilder : ItemTarget(mapOf()) {
+
+ val children = ArrayList<Node>()
+
+ fun addApp(packageName: String, className: String?): FolderBuilder {
+ putApp(packageName, className)
+ return this
+ }
+
+ fun addShortcut(packageName: String, shortcutId: String): FolderBuilder {
+ putShortcut(packageName, shortcutId)
+ return this
+ }
+
+ override fun addItem(
+ tag: String,
+ userType: String?,
+ props: Map<String, String>,
+ childrenIgnored: List<Node>?
+ ): LauncherLayoutBuilder {
+ children.add(
+ Node(tag, HashMap(props).apply { userType?.let { put(ATTR_USER_TYPE, it) } })
+ )
+ return this@LauncherLayoutBuilder
+ }
+
+ override fun putFolder(titleKey: String, titleValue: String?): FolderBuilder {
+ throw IllegalArgumentException("Can't have folder inside a folder")
+ }
+
+ fun build() = this@LauncherLayoutBuilder
+ }
+
+ @Throws(IOException::class)
+ private fun XmlSerializer.writeNodes(nodes: List<Node>) {
+ nodes.forEach { node ->
+ startTag(null, node.name)
+ node.attrs.forEach { (key, value) -> attribute(null, key, value) }
+ node.children?.let { writeNodes(it) }
+ endTag(null, node.name)
+ }
+ }
+
+ data class Node(
+ val name: String,
+ val attrs: Map<String, String>,
+ val children: List<Node>? = null
+ )
+}
diff --git a/src/com/android/launcher3/util/rects/Rects.kt b/src/com/android/launcher3/util/rects/Rects.kt
new file mode 100644
index 0000000..1e6d717
--- /dev/null
+++ b/src/com/android/launcher3/util/rects/Rects.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util.rects
+
+import android.graphics.Rect
+import android.view.View
+
+/** Copy the coordinates of the [view] relative to its parent into this rectangle. */
+fun Rect.set(view: View) {
+ set(0, 0, view.width, view.height)
+ offset(view.x.toInt(), view.y.toInt())
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 11177de..cc0021b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -199,8 +199,8 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"uiautomator-helpers",
- "mockito-robolectric-prebuilt",
- "mockito-kotlin2",
+ "inline-mockito-robolectric-prebuilt",
+ "mockito-kotlin-nodeps",
"platform-parametric-runner-lib",
"testables",
"Launcher3TestResources",
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 8c5195e..4b926a8 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -407,7 +407,7 @@
</intent-filter>
</activity>
- <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
+ <!-- Disable eager initialization of Jetpack libraries. See bug 197780098. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
diff --git a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
new file mode 100644
index 0000000..b04bcca
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3
+
+import android.content.ComponentName
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import android.os.Process.myUserHandle
+import android.os.UserHandle
+import android.util.Xml
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback
+import com.android.launcher3.AutoInstallsLayout.SourceResources
+import com.android.launcher3.AutoInstallsLayout.TAG_WORKSPACE
+import com.android.launcher3.AutoInstallsLayout.USER_TYPE_WORK
+import com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
+import com.android.launcher3.LauncherSettings.Favorites.INTENT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID
+import com.android.launcher3.LauncherSettings.Favorites.SPANX
+import com.android.launcher3.LauncherSettings.Favorites.SPANY
+import com.android.launcher3.LauncherSettings.Favorites._ID
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.util.ApiWrapper
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.LauncherLayoutBuilder
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
+import com.android.launcher3.util.TestUtil
+import com.android.launcher3.util.UserIconInfo
+import com.android.launcher3.util.UserIconInfo.TYPE_MAIN
+import com.android.launcher3.util.UserIconInfo.TYPE_WORK
+import com.android.launcher3.widget.LauncherWidgetHolder
+import com.google.common.truth.Truth.assertThat
+import java.io.StringReader
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+/** Tests for [AutoInstallsLayout] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AutoInstallsLayoutTest {
+
+ lateinit var modelHelper: LauncherModelHelper
+ lateinit var targetContext: SandboxModelContext
+
+ lateinit var callback: MyCallback
+
+ @Mock lateinit var widgetHolder: LauncherWidgetHolder
+ @Mock lateinit var db: SQLiteDatabase
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ modelHelper = LauncherModelHelper()
+ targetContext = modelHelper.sandboxContext
+ callback = MyCallback()
+ }
+
+ @After
+ fun tearDown() {
+ modelHelper.destroy()
+ }
+
+ @Test
+ fun pending_icon_added_on_home() {
+ LauncherLayoutBuilder()
+ .atWorkspace(1, 1, 0)
+ .putApp("p1", "c1")
+ .toAutoInstallsLayout()
+ .loadLayout(db)
+
+ assertThat(callback.items.size).isEqualTo(1)
+ assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION)
+ assertThat(callback.items[0][INTENT])
+ .isEqualTo(AppInfo.makeLaunchIntent(ComponentName("p1", "c1")).toUri(0))
+ assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP)
+ assertThat(callback.items[0].containsKey(PROFILE_ID)).isFalse()
+ }
+
+ @Test
+ fun pending_icon_added_on_hotseat() {
+ LauncherLayoutBuilder()
+ .atHotseat(1)
+ .putApp("p1", "c1")
+ .toAutoInstallsLayout()
+ .loadLayout(db)
+
+ assertThat(callback.items.size).isEqualTo(1)
+ assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION)
+ assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_HOTSEAT)
+ }
+
+ @Test
+ fun widget_added_to_home() {
+ LauncherLayoutBuilder()
+ .atWorkspace(1, 1, 0)
+ .putWidget("p1", "c1", 2, 3)
+ .toAutoInstallsLayout()
+ .loadLayout(db)
+
+ assertThat(callback.items.size).isEqualTo(1)
+ assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPWIDGET)
+ assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP)
+ assertThat(callback.items[0][APPWIDGET_PROVIDER])
+ .isEqualTo(ComponentName("p1", "c1").flattenToString())
+ assertThat(callback.items[0][SPANX]).isEqualTo(2.toString())
+ assertThat(callback.items[0][SPANY]).isEqualTo(3.toString())
+ }
+
+ @Test
+ fun items_added_to_folder() {
+ LauncherLayoutBuilder()
+ .atHotseat(1)
+ .putFolder("Test")
+ .addApp("p1", "c")
+ .addApp("p2", "c")
+ .addApp("p3", "c")
+ .build()
+ .toAutoInstallsLayout()
+ .loadLayout(db)
+
+ assertThat(callback.items.size).isEqualTo(4)
+ assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_FOLDER)
+ assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_HOTSEAT)
+
+ val folderId = callback.items[0][_ID]
+ assertThat(callback.items[1][CONTAINER]).isEqualTo(folderId)
+ assertThat(callback.items[2][CONTAINER]).isEqualTo(folderId)
+ assertThat(callback.items[3][CONTAINER]).isEqualTo(folderId)
+ }
+
+ @Test
+ fun work_item_added_to_home() {
+ val apiWrapperMock = spy(ApiWrapper.INSTANCE[targetContext])
+ targetContext.putObject(ApiWrapper.INSTANCE, apiWrapperMock)
+ doReturn(
+ mapOf(
+ myUserHandle() to UserIconInfo(myUserHandle(), TYPE_MAIN, 0),
+ UserHandle.of(20) to UserIconInfo(UserHandle.of(20), TYPE_WORK, 20),
+ )
+ )
+ .whenever(apiWrapperMock)
+ .queryAllUsers()
+
+ val cache = UserCache.getInstance(targetContext)
+ TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
+ assertThat(cache.userProfiles.size).isEqualTo(2)
+ }
+
+ LauncherLayoutBuilder()
+ .atWorkspace(1, 1, 0)
+ .putApp("p1", "c1", USER_TYPE_WORK)
+ .toAutoInstallsLayout()
+ .loadLayout(db)
+
+ assertThat(callback.items.size).isEqualTo(1)
+ assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION)
+ assertThat(callback.items[0][INTENT])
+ .isEqualTo(AppInfo.makeLaunchIntent(ComponentName("p1", "c1")).toUri(0))
+ assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP)
+ assertThat(callback.items[0][PROFILE_ID]).isEqualTo(20)
+ }
+
+ private fun LauncherLayoutBuilder.toAutoInstallsLayout() =
+ AutoInstallsLayout(
+ targetContext,
+ widgetHolder,
+ callback,
+ SourceResources.wrap(targetContext.resources),
+ { Xml.newPullParser().also { it.setInput(StringReader(build())) } },
+ TAG_WORKSPACE
+ )
+
+ class MyCallback : LayoutParserCallback {
+
+ val items = ArrayList<ContentValues>()
+
+ override fun generateNewItemId() = items.size
+
+ override fun insertAndCheck(db: SQLiteDatabase?, values: ContentValues): Int {
+ val id = values[_ID]
+ items.add(ContentValues(values))
+ return if (id is Int) id else 0
+ }
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
deleted file mode 100644
index ba01b04..0000000
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.android.launcher3.util;
-
-
-import android.text.TextUtils;
-import android.util.Pair;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Helper class to build xml for Launcher Layout
- */
-public class LauncherLayoutBuilder {
-
- // Object Tags
- private static final String TAG_WORKSPACE = "workspace";
- private static final String TAG_AUTO_INSTALL = "autoinstall";
- private static final String TAG_FOLDER = "folder";
- private static final String TAG_APPWIDGET = "appwidget";
- private static final String TAG_SHORTCUT = "shortcut";
- private static final String TAG_EXTRA = "extra";
-
- private static final String ATTR_CONTAINER = "container";
- private static final String ATTR_RANK = "rank";
-
- private static final String ATTR_PACKAGE_NAME = "packageName";
- private static final String ATTR_CLASS_NAME = "className";
- private static final String ATTR_TITLE = "title";
- private static final String ATTR_TITLE_TEXT = "titleText";
- private static final String ATTR_SCREEN = "screen";
- private static final String ATTR_SHORTCUT_ID = "shortcutId";
-
- // x and y can be specified as negative integers, in which case -1 represents the
- // last row / column, -2 represents the second last, and so on.
- private static final String ATTR_X = "x";
- private static final String ATTR_Y = "y";
- private static final String ATTR_SPAN_X = "spanX";
- private static final String ATTR_SPAN_Y = "spanY";
-
- private static final String ATTR_CHILDREN = "children";
-
-
- // Style attrs -- "Extra"
- private static final String ATTR_KEY = "key";
- private static final String ATTR_VALUE = "value";
-
- private static final String CONTAINER_DESKTOP = "desktop";
- private static final String CONTAINER_HOTSEAT = "hotseat";
-
- private final ArrayList<Pair<String, HashMap<String, Object>>> mNodes = new ArrayList<>();
-
- public Location atHotseat(int rank) {
- Location l = new Location();
- l.items.put(ATTR_CONTAINER, CONTAINER_HOTSEAT);
- l.items.put(ATTR_RANK, Integer.toString(rank));
- return l;
- }
-
- public Location atWorkspace(int x, int y, int screen) {
- Location l = new Location();
- l.items.put(ATTR_CONTAINER, CONTAINER_DESKTOP);
- l.items.put(ATTR_X, Integer.toString(x));
- l.items.put(ATTR_Y, Integer.toString(y));
- l.items.put(ATTR_SCREEN, Integer.toString(screen));
- return l;
- }
-
- public String build() throws IOException {
- StringWriter writer = new StringWriter();
- build(writer);
- return writer.toString();
- }
-
- public void build(Writer writer) throws IOException {
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(writer);
-
- serializer.startDocument("UTF-8", true);
- serializer.startTag(null, TAG_WORKSPACE);
- writeNodes(serializer, mNodes);
- serializer.endTag(null, TAG_WORKSPACE);
- serializer.endDocument();
- serializer.flush();
- }
-
- private static void writeNodes(XmlSerializer serializer,
- ArrayList<Pair<String, HashMap<String, Object>>> nodes) throws IOException {
- for (Pair<String, HashMap<String, Object>> node : nodes) {
- ArrayList<Pair<String, HashMap<String, Object>>> children = null;
-
- serializer.startTag(null, node.first);
- for (Map.Entry<String, Object> attr : node.second.entrySet()) {
- if (ATTR_CHILDREN.equals(attr.getKey())) {
- children = (ArrayList<Pair<String, HashMap<String, Object>>>) attr.getValue();
- } else {
- serializer.attribute(null, attr.getKey(), (String) attr.getValue());
- }
- }
-
- if (children != null) {
- writeNodes(serializer, children);
- }
- serializer.endTag(null, node.first);
- }
- }
-
- public class Location {
-
- final HashMap<String, Object> items = new HashMap<>();
-
- public LauncherLayoutBuilder putApp(String packageName, String className) {
- items.put(ATTR_PACKAGE_NAME, packageName);
- items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
- mNodes.add(Pair.create(TAG_AUTO_INSTALL, items));
- return LauncherLayoutBuilder.this;
- }
-
- public LauncherLayoutBuilder putShortcut(String packageName, String shortcutId) {
- items.put(ATTR_PACKAGE_NAME, packageName);
- items.put(ATTR_SHORTCUT_ID, shortcutId);
- mNodes.add(Pair.create(TAG_SHORTCUT, items));
- return LauncherLayoutBuilder.this;
- }
-
- public LauncherLayoutBuilder putWidget(String packageName, String className,
- int spanX, int spanY) {
- items.put(ATTR_PACKAGE_NAME, packageName);
- items.put(ATTR_CLASS_NAME, className);
- items.put(ATTR_SPAN_X, Integer.toString(spanX));
- items.put(ATTR_SPAN_Y, Integer.toString(spanY));
- mNodes.add(Pair.create(TAG_APPWIDGET, items));
- return LauncherLayoutBuilder.this;
- }
-
- public FolderBuilder putFolder(int titleResId) {
- items.put(ATTR_TITLE, Integer.toString(titleResId));
- return putFolder();
- }
-
- public FolderBuilder putFolder(String title) {
- items.put(ATTR_TITLE_TEXT, title);
- return putFolder();
- }
-
- private FolderBuilder putFolder() {
- FolderBuilder folderBuilder = new FolderBuilder();
- items.put(ATTR_CHILDREN, folderBuilder.mChildren);
- mNodes.add(Pair.create(TAG_FOLDER, items));
- return folderBuilder;
- }
- }
-
- public class FolderBuilder {
-
- final ArrayList<Pair<String, HashMap<String, Object>>> mChildren = new ArrayList<>();
-
- public FolderBuilder addApp(String packageName, String className) {
- HashMap<String, Object> items = new HashMap<>();
- items.put(ATTR_PACKAGE_NAME, packageName);
- items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
- mChildren.add(Pair.create(TAG_AUTO_INSTALL, items));
- return this;
- }
-
- public FolderBuilder addShortcut(String packageName, String shortcutId) {
- HashMap<String, Object> items = new HashMap<>();
- items.put(ATTR_PACKAGE_NAME, packageName);
- items.put(ATTR_SHORTCUT_ID, shortcutId);
- mChildren.add(Pair.create(TAG_SHORTCUT, items));
- return this;
- }
-
- public LauncherLayoutBuilder build() {
- return LauncherLayoutBuilder.this;
- }
- }
-}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java b/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java
index 027a31a..deb0ef3 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/WidgetUtils.java
@@ -15,15 +15,12 @@
*/
package com.android.launcher3.util;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.os.Bundle;
-import android.os.Process;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -87,10 +84,10 @@
* Creates a {@link AppWidgetProviderInfo} for the provided component name
*/
public static AppWidgetProviderInfo createAppWidgetProviderInfo(ComponentName cn) {
- AppWidgetProviderInfo info = AppWidgetManager.getInstance(getApplicationContext())
- .getInstalledProvidersForPackage(
- getInstrumentation().getContext().getPackageName(), Process.myUserHandle())
- .get(0);
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = new ApplicationInfo();
+ AppWidgetProviderInfo info = new AppWidgetProviderInfo();
+ info.providerInfo = activityInfo;
info.provider = cn;
return info;
}
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
similarity index 92%
rename from tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
index 7476454..3024d26 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
@@ -25,7 +25,6 @@
import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
@@ -35,7 +34,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
-import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -53,6 +51,7 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.google.common.collect.ImmutableMap;
@@ -152,11 +151,8 @@
doAnswer(invocation -> widgetLabel).when(mIconCache).getTitleNoCache(any());
- AppWidgetProviderInfo providerInfo = AppWidgetManager.getInstance(getApplicationContext())
- .getInstalledProvidersForPackage(
- getInstrumentation().getContext().getPackageName(), Process.myUserHandle())
- .get(0);
- providerInfo.provider = ComponentName.createRelative(TEST_PACKAGE, widgetClassName);
+ AppWidgetProviderInfo providerInfo = WidgetUtils.createAppWidgetProviderInfo(ComponentName
+ .createRelative(TEST_PACKAGE, widgetClassName));
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, providerInfo);
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
diff --git a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 9c03ccf..0370a6b 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -17,6 +17,7 @@
package com.android.launcher3.widget.picker.search;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
@@ -163,7 +164,7 @@
.when(mDataProvider)
.getAllWidgets();
mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
- MAIN_EXECUTOR.submit(() -> { }).get();
+ getInstrumentation().waitForIdleSync();
verify(mSearchCallback).onSearchResult(
matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
}
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
similarity index 93%
rename from tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
index 040fbf5..7b629bf 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
@@ -43,6 +43,7 @@
private lateinit var context: Context
private lateinit var deviceProfile: DeviceProfile
private lateinit var testInvariantProfile: InvariantDeviceProfile
+ private lateinit var widgetItemInvariantProfile: InvariantDeviceProfile
@Mock private lateinit var iconCache: IconCache
@@ -51,6 +52,11 @@
MockitoAnnotations.initMocks(this)
context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
testInvariantProfile = LauncherAppState.getIDP(context)
+ widgetItemInvariantProfile =
+ InvariantDeviceProfile().apply {
+ numRows = TEST_GRID_SIZE
+ numColumns = TEST_GRID_SIZE
+ }
deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
}
@@ -60,7 +66,8 @@
val expectedPreviewContainers = testSizes.values.toList()
for ((index, widgetSize) in testSizes.keys.withIndex()) {
- val widgetItem = createWidgetItem(widgetSize, context, testInvariantProfile, iconCache)
+ val widgetItem =
+ createWidgetItem(widgetSize, context, widgetItemInvariantProfile, iconCache)
assertWithMessage("size for $widgetSize should be: ${expectedPreviewContainers[index]}")
.that(WidgetPreviewContainerSize.forItem(widgetItem, deviceProfile))
@@ -70,6 +77,7 @@
companion object {
private const val TEST_PACKAGE = "com.google.test"
+ private const val TEST_GRID_SIZE = 6
private val HANDHELD_TEST_SIZES: Map<Point, WidgetPreviewContainerSize> =
mapOf(
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
rename to tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
diff --git a/tests/src/com/android/launcher3/AppFilterTest.kt b/tests/src/com/android/launcher3/AppFilterTest.kt
new file mode 100644
index 0000000..f2150a2
--- /dev/null
+++ b/tests/src/com/android/launcher3/AppFilterTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.res.Resources
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+
+@RunWith(MockitoJUnitRunner::class)
+class AppFilterTest {
+
+ @Mock private lateinit var mockContext: Context
+
+ @Mock // Mock the Resources object as well
+ private lateinit var mockResources: Resources
+
+ private lateinit var appFilter: AppFilter
+
+ @Before
+ fun setUp() {
+ `when`(mockContext.resources).thenReturn(mockResources) // Link the context and resources
+ `when`(mockResources.getStringArray(R.array.filtered_components))
+ .thenReturn(arrayOf("com.example.app1/Activity1"))
+ appFilter = AppFilter(mockContext)
+ }
+
+ @Test
+ fun shouldShowApp_notFiltered_returnsTrue() {
+ val appToShow = ComponentName("com.example.app2", "Activity2")
+ assertThat(appFilter.shouldShowApp(appToShow)).isTrue()
+ }
+
+ @Test
+ fun shouldShowApp_filtered_returnsFalse() {
+ val appToHide = ComponentName("com.example.app1", "Activity1")
+ assertThat(appFilter.shouldShowApp(appToHide)).isFalse()
+ }
+}
diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
index 13d7499..0538870 100644
--- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
@@ -46,7 +46,7 @@
@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class FakeInvariantDeviceProfileTest {
- protected var context: Context? = null
+ protected lateinit var context: Context
protected var inv: InvariantDeviceProfile? = null
protected val info: Info = mock()
protected var windowBounds: WindowBounds? = null
@@ -257,10 +257,10 @@
}
protected fun initializeVarsForTwoPanel(
- isLandscape: Boolean = false,
- isGestureMode: Boolean = true,
- rows: Int = 4,
- cols: Int = 4,
+ isLandscape: Boolean = false,
+ isGestureMode: Boolean = true,
+ rows: Int = 4,
+ cols: Int = 4,
) {
val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208)
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index 6bbcf85..6cf3b19 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -23,6 +23,7 @@
import android.content.pm.LauncherApps
import android.content.pm.PackageInstaller
import android.content.pm.ShortcutInfo
+import android.os.Process
import android.os.UserHandle
import android.platform.test.annotations.EnableFlags
import android.util.LongSparseArray
@@ -429,6 +430,7 @@
whenever(disabledMessage).thenReturn("")
whenever(disabledReason).thenReturn(0)
whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
+ whenever(userHandle).thenReturn(Process.myUserHandle())
}
mIconRequestInfos = mutableListOf()
// Make sure shortcuts map has expected key from expected package
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index dc8c17a..c663be0 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -57,6 +57,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.util.TestSandboxModelContextWrapper;
@@ -88,6 +89,7 @@
private PopupDataProvider mPopupDataProvider;
private AppInfo mAppInfo;
@Mock UserCache mUserCache;
+ @Mock ApiWrapper mApiWrapper;
@Mock BaseDragLayer mBaseDragLayer;
@Mock UserIconInfo mUserIconInfo;
@Mock LauncherActivityInfo mLauncherActivityInfo;
@@ -98,6 +100,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
+ mSandboxContext.putObject(ApiWrapper.INSTANCE, mApiWrapper);
mTestContext = new TestSandboxModelContextWrapper(mSandboxContext);
mView = new View(mSandboxContext);
spyOn(mTestContext);
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9dbd866..9b184ae 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -32,7 +32,6 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -54,7 +53,6 @@
@Test
@PortraitLandscape
- @ScreenRecord // b/316910614
public void testDragIcon() throws Throwable {
mLauncher.enableDebugTracing(); // b/289161193
commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
@@ -107,7 +105,6 @@
@Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/316910614
@PlatinumTest(focusArea = "launcher")
@Test
- @ScreenRecord // b/316910614
public void testResizeWidget() throws Throwable {
commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));