Merge "Revert "Enable taskbar in overview by default"" into tm-qpr-dev
diff --git a/go/quickstep/res/values-my/strings.xml b/go/quickstep/res/values-my/strings.xml
index 0ca0e9c..cbb485a 100644
--- a/go/quickstep/res/values-my/strings.xml
+++ b/go/quickstep/res/values-my/strings.xml
@@ -5,7 +5,7 @@
<string name="action_listen" msgid="2370304050784689486">"နားထောင်ရန်"</string>
<string name="action_translate" msgid="8028378961867277746">"ဘာသာပြန်ရန်"</string>
<string name="action_search" msgid="6269564710943755464">"Lens"</string>
- <string name="dialog_acknowledge" msgid="2804025517675853172">"ရပြီ"</string>
+ <string name="dialog_acknowledge" msgid="2804025517675853172">"နားလည်ပြီ"</string>
<string name="dialog_cancel" msgid="6464336969134856366">"မလုပ်တော့"</string>
<string name="dialog_settings" msgid="6564397136021186148">"ဆက်တင်များ"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ပါ (သို့) နားထောင်ပါ"</string>
diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml
new file mode 100644
index 0000000..0c8543f
--- /dev/null
+++ b/quickstep/res/layout/task_desktop.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.quickstep.views.DesktopTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ android:clipToOutline="true"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true">
+
+ <!--
+ TODO(b249371338): DesktopTaskView extends from TaskView. TaskView expects TaskThumbnailView
+ and IconView with these ids to be present. Need to refactor RecentsView to accept child
+ views that do not inherint from TaskView only or create a generic TaskView that have
+ N number of tasks.
+ -->
+ <com.android.quickstep.views.TaskThumbnailView
+ android:id="@+id/snapshot"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <com.android.quickstep.views.IconView
+ android:id="@+id/icon"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:visibility="gone" />
+
+</com.android.quickstep.views.DesktopTaskView>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 6ea7d8a..1789c2b 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"ነጻ ቅጽ"</string>
+ <string name="recent_task_option_freeform" msgid="48863056265284071">"ነፃ ቅጽ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 905fbda..bc5d02a 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -80,4 +80,8 @@
<dimen name="taskbar_button_margin_6_5">219.6dp</dimen>
<dimen name="taskbar_button_margin_4_5">84dp</dimen>
<dimen name="taskbar_button_margin_4_4">79dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">48dp</dimen>
+ <dimen name="taskbar_suw_frame">96dp</dimen>
+ <dimen name="taskbar_suw_insets">24dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 765d36c..3add4dc 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -258,7 +258,10 @@
<dimen name="taskbar_contextual_button_padding">16dp</dimen>
<dimen name="taskbar_contextual_padding_top">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">44dp</dimen>
- <dimen name="taskbar_contextual_button_margin">48dp</dimen>
+ <dimen name="taskbar_split_instructions_margin">48dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">120dp</dimen>
+ <dimen name="taskbar_suw_insets">48dp</dimen>
+ <dimen name="taskbar_suw_frame">48dp</dimen>
<dimen name="taskbar_hotseat_nav_spacing">24dp</dimen>
<dimen name="taskbar_contextual_buttons_size">35dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
@@ -284,4 +287,9 @@
<dimen name="taskbar_button_margin_4_5">47dp</dimen>
<dimen name="taskbar_button_margin_4_4">47dp</dimen>
<dimen name="taskbar_button_margin_default">47dp</dimen>
+
+ <!-- Launcher splash screen -->
+ <!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
+ <!-- starting_surface_exit_animation_window_shift_length -->
+ <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 2100834..80bdb6f 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -38,6 +38,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.views.AbstractSlideInView;
@@ -192,7 +193,7 @@
icon.setEnabled(false);
icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
icon.verifyHighRes();
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1);
mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 5178968..b9b4fc3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -173,7 +173,7 @@
}
}
- if (ENABLE_SHELL_TRANSITIONS
+ if (ENABLE_SHELL_TRANSITIONS && isResumed
&& !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
// Launcher is resumed, but in a state where taskbar is still independent, so
// ignore the state change.
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 725ce11..339f3a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -204,8 +204,10 @@
DeviceProfile deviceProfile = mContext.getDeviceProfile();
Resources resources = mContext.getResources();
mNavButtonsView.getLayoutParams().height = !isPhoneMode(deviceProfile) ?
- deviceProfile.taskbarSize :
- resources.getDimensionPixelSize(R.dimen.taskbar_size);
+ mContext.isUserSetupComplete()
+ ? deviceProfile.taskbarSize
+ : resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame)
+ : resources.getDimensionPixelSize(R.dimen.taskbar_size);
mIsImeRenderingNavButtons =
InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
@@ -267,15 +269,7 @@
updateButtonLayoutSpacing();
updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext));
if (isInSetup) {
- // Since setup wizard only has back button enabled, it looks strange to be
- // end-aligned, so start-align instead.
- FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
- mNavButtonContainer.getLayoutParams();
- navButtonsLayoutParams.setMarginStart(
- resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin));
- navButtonsLayoutParams.setMarginEnd(0);
- navButtonsLayoutParams.gravity = Gravity.START;
- mNavButtonContainer.requestLayout();
+ handleSetupUi();
// Hide back button in SUW if keyboard is showing (IME draws its own back).
mPropertyHolders.add(new StatePropertyHolder(
@@ -745,14 +739,38 @@
if (mFloatingRotationButton != null) {
mFloatingRotationButton.onConfigurationChanged(configChanges);
}
+ if (!mContext.isUserSetupComplete()) {
+ handleSetupUi();
+ }
updateButtonLayoutSpacing();
}
+ private void handleSetupUi() {
+ // Since setup wizard only has back button enabled, it looks strange to be
+ // end-aligned, so start-align instead.
+ FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+ mNavButtonContainer.getLayoutParams();
+ Resources resources = mContext.getResources();
+ DeviceProfile deviceProfile = mContext.getDeviceProfile();
+ int setupMargin = resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin);
+ navButtonsLayoutParams.setMarginStart(setupMargin);
+ navButtonsLayoutParams.bottomMargin = !deviceProfile.isLandscape
+ ? 0
+ : setupMargin -
+ (resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2);
+ navButtonsLayoutParams.setMarginEnd(0);
+ navButtonsLayoutParams.gravity = Gravity.START;
+ mNavButtonContainer.setLayoutParams(navButtonsLayoutParams);
+ mNavButtonContainer.requestLayout();
+ }
+
/**
- * Adds the correct spacing to 3 button nav container. No-op if using gesture nav or kids mode.
+ * Adds the correct spacing to 3 button nav container. No-op if using gesture nav, setup
+ * is incomplete, or in kids mode.
*/
private void updateButtonLayoutSpacing() {
- if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()) {
+ if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()
+ || !mContext.isUserSetupComplete()) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9d15ea8..72c163e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -89,6 +89,7 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
import java.io.PrintWriter;
@@ -148,11 +149,12 @@
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
+ mIsUserSetupComplete = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
- mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(
+ mIsNavBarForceVisible = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
- mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
+ mIsNavBarKidsMode = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
updateIconSize(resources);
@@ -197,7 +199,9 @@
new TaskbarViewController(this, taskbarView),
new TaskbarScrimViewController(this, taskbarScrimView),
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
- mWindowManager, WindowManagerGlobal.getWindowManagerService()),
+ mWindowManager,
+ new RotationChangeProvider(WindowManagerGlobal.getWindowManagerService(), this,
+ getMainExecutor())),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
@@ -614,6 +618,10 @@
resources.getDimensionPixelSize(R.dimen.taskbar_size) :
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
+
+ if (!isUserSetupComplete()) {
+ return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+ }
return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index b9bcf1b..9fd2bf9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -242,6 +242,7 @@
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
+ && isInApp()
&& (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests);
}
@@ -354,6 +355,11 @@
}
return mStashedHeight;
}
+
+ if (!mActivity.isUserSetupComplete()) {
+ // Special insets for SUW.
+ return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
+ }
return mUnstashedHeight;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
index 64a4fa7..4c937a7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -15,13 +15,13 @@
*/
package com.android.launcher3.taskbar;
-import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -41,16 +41,20 @@
public TaskbarUnfoldAnimationController(BaseTaskbarContext context,
ScopedUnfoldTransitionProgressProvider source,
- WindowManager windowManager, IWindowManager iWindowManager) {
+ WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mScopedUnfoldTransitionProgressProvider = source;
mNaturalUnfoldTransitionProgressProvider =
- new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source);
+ new NaturalRotationUnfoldProgressProvider(context,
+ rotationChangeProvider,
+ source);
mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
}
/**
* Initializes the controller
+ *
* @param taskbarControllers references to all other taskbar controllers
*/
public void init(TaskbarControllers taskbarControllers) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 5f3a990..bf0f8f7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -49,6 +49,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -271,7 +272,7 @@
mIsPinned = true;
applyFromWorkspaceItem(info);
setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = true;
invalidate();
}
@@ -280,7 +281,7 @@
*/
public void finishBinding(OnLongClickListener longClickListener) {
setOnLongClickListener(longClickListener);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = false;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = false;
setTextVisibility(false);
verifyHighRes();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4ca27b1..ee9845b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -66,6 +66,7 @@
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.View;
+import android.view.WindowManagerGlobal;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -139,12 +140,14 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.unfold.UnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
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.system.ActivityManagerActivityTypeProvider;
import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -173,6 +176,7 @@
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+ private @Nullable RotationChangeProvider mRotationChangeProvider;
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
/**
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
@@ -664,8 +668,8 @@
private void initUnfoldTransitionProgressProvider() {
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
if (config.isEnabled()) {
- mUnfoldTransitionProgressProvider =
- UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
+ UnfoldSharedComponent unfoldComponent =
+ UnfoldTransitionFactory.createUnfoldSharedComponent(
/* context= */ this,
config,
ProxyScreenStatusProvider.INSTANCE,
@@ -677,13 +681,21 @@
getMainThreadHandler(),
getMainExecutor(),
/* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
- /* tracingTagPrefix= */ "launcher"
+ /* tracingTagPrefix= */ "launcher",
+ WindowManagerGlobal.getWindowManagerService()
);
+ mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider()
+ .orElseThrow(() -> new IllegalStateException(
+ "Trying to create UnfoldTransitionProgressProvider when the "
+ + "transition is disabled"));
+
+ mRotationChangeProvider = unfoldComponent.getRotationChangeProvider();
mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
- this,
+ /* launcher= */ this,
getWindowManager(),
- mUnfoldTransitionProgressProvider
+ mUnfoldTransitionProgressProvider,
+ mRotationChangeProvider
);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 3fe2e59..0e1120b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -90,9 +90,9 @@
// While animating into recents, update the visible task data as needed
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
- // TODO(b/238461210): Remove logging once root cause of flake detected.
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d("b/238461210", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ mRecentsView.getCurrentPage()
+ ", getScrollForPage(getCurrentPage())): "
+ mRecentsView.getScrollForPage(mRecentsView.getCurrentPage()));
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 9106a8f..07ddcc8 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -75,6 +75,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowInsets;
@@ -127,12 +128,17 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -248,6 +254,13 @@
private static final float MAX_QUICK_SWITCH_RECENTS_SCALE_PROGRESS = 0.07f;
+ // Controls task thumbnail splash's reveal animation after landing on a task from quickswitch.
+ // These values match WindowManager/Shell starting_window_app_reveal_* config values.
+ private static final int SPLASH_FADE_OUT_DURATION = 133;
+ private static final int SPLASH_APP_REVEAL_DELAY = 83;
+ private static final int SPLASH_APP_REVEAL_DURATION = 266;
+ private static final int SPLASH_ANIMATION_DURATION = 349;
+
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
*/
@@ -272,7 +285,7 @@
private AnimatorControllerWithResistance mLauncherTransitionController;
private boolean mHasEndedLauncherTransition;
- private AnimationFactory mAnimationFactory = (t) -> { };
+ private AnimationFactory mAnimationFactory = (t, s) -> { };
private boolean mWasLauncherAlreadyVisible;
@@ -285,6 +298,8 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
+ private final int mSplashMainWindowShiftLength;
+
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
@@ -320,6 +335,9 @@
mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
R.dimen.quick_switch_scaling_scroll_threshold);
+ mSplashMainWindowShiftLength = -context.getResources().getDimensionPixelSize(
+ R.dimen.starting_surface_exit_animation_window_shift_length);
+
initAfterSubclassConstructor();
initStateCallbacks();
}
@@ -501,7 +519,9 @@
Runnable initAnimFactory = () -> {
mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState,
mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
- maybeUpdateRecentsAttachedState(false /* animate */);
+ maybeUpdateRecentsAttachedState(
+ false /* animate */,
+ new ActiveGestureLog.CompoundString("on Launcher start (animate=false)"));
if (mGestureState.getEndTarget() != null) {
// Update the end target in case the gesture ended before we init.
mAnimationFactory.setEndTarget(mGestureState.getEndTarget());
@@ -606,7 +626,8 @@
}
private void initializeLauncherAnimationController() {
- buildAnimationController();
+ buildAnimationController(new ActiveGestureLog.CompoundString(
+ "initializing launcher animation controller"));
Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
TraceHelper.FLAG_IGNORE_BINDERS);
@@ -625,7 +646,11 @@
@Override
public void onMotionPauseDetected() {
mHasMotionEverBeenPaused = true;
- maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
+ maybeUpdateRecentsAttachedState(
+ true/* animate */,
+ true/* moveFocusedTask */,
+ new ActiveGestureLog.CompoundString(
+ "motion pause detected (animate=true)"));
performHapticFeedback();
}
@@ -636,12 +661,13 @@
};
}
- private void maybeUpdateRecentsAttachedState() {
- maybeUpdateRecentsAttachedState(true /* animate */);
+ private void maybeUpdateRecentsAttachedState(ActiveGestureLog.CompoundString reason) {
+ maybeUpdateRecentsAttachedState(true /* animate */, reason.append(" (animate=true)"));
}
- private void maybeUpdateRecentsAttachedState(boolean animate) {
- maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */);
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, ActiveGestureLog.CompoundString reason) {
+ maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */, reason);
}
/**
@@ -653,7 +679,8 @@
* @param animate whether to animate when attaching RecentsView
* @param moveFocusedTask whether to move focused task to front when attaching
*/
- private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveFocusedTask) {
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, boolean moveFocusedTask, ActiveGestureLog.CompoundString reason) {
if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
@@ -663,14 +690,25 @@
final boolean recentsAttachedToAppWindow;
if (mGestureState.getEndTarget() != null) {
recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
+ reason.append("; gesture state end target != null (attached=")
+ .append(Boolean.toString(recentsAttachedToAppWindow))
+ .append(")");
} else if (mContinuingLastGesture
&& mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
recentsAttachedToAppWindow = true;
+ reason.append("; continuing last gesture (attached=true)");
} else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
// The window is going away so make sure recents is always visible in this case.
recentsAttachedToAppWindow = true;
+ reason.append("; make sure recents is always visible (attached=true)");
} else {
recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
+ reason.append(mHasMotionEverBeenPaused
+ ? "; motion has been paused"
+ : "; gesture is likely to start a new task")
+ .append(" (attached=")
+ .append(Boolean.toString(recentsAttachedToAppWindow))
+ .append(")");
}
if (moveFocusedTask && !mAnimationFactory.hasRecentsEverAttachedToAppWindow()
&& recentsAttachedToAppWindow) {
@@ -678,7 +716,8 @@
// TaskView jumping to new position as we move the tasks.
mRecentsView.moveFocusedTaskToFront();
}
- mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
+ mAnimationFactory.setRecentsAttachedToAppWindow(
+ recentsAttachedToAppWindow, animate, reason);
// Reapply window transform throughout the attach animation, as the animation affects how
// much the window is bound by overscroll (vs moving freely).
@@ -698,22 +737,29 @@
}
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
- setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
+ setIsLikelyToStartNewTask(
+ isLikelyToStartNewTask,
+ true /* animate */,
+ new ActiveGestureLog.CompoundString(
+ "setting gesture likely to start (animate=true)"));
}
- private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
+ private void setIsLikelyToStartNewTask(
+ boolean isLikelyToStartNewTask,
+ boolean animate,
+ ActiveGestureLog.CompoundString reason) {
if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
mIsLikelyToStartNewTask = isLikelyToStartNewTask;
- maybeUpdateRecentsAttachedState(animate);
+ maybeUpdateRecentsAttachedState(animate, reason);
}
}
- private void buildAnimationController() {
+ private void buildAnimationController(ActiveGestureLog.CompoundString reason) {
if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
- mAnimationFactory.createActivityInterface(mTransitionDragLength);
+ mAnimationFactory.createActivityInterface(mTransitionDragLength, reason);
}
/**
@@ -728,7 +774,7 @@
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
WindowInsets result = view.onApplyWindowInsets(windowInsets);
- buildAnimationController();
+ buildAnimationController(new ActiveGestureLog.CompoundString("applying window insets"));
// Reapply the current shift to ensure it takes new insets into account, e.g. when long
// pressing to stash taskbar without moving the finger.
updateFinalShift();
@@ -900,7 +946,10 @@
});
}
notifyGestureStartedAsync();
- setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
+ setIsLikelyToStartNewTask(
+ isLikelyToStartNewTask,
+ false /* animate */,
+ new ActiveGestureLog.CompoundString("on gesture started (animate=false)"));
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
@@ -982,7 +1031,8 @@
private void onSettledOnEndTarget() {
// Fast-finish the attaching animation if it's still running.
- maybeUpdateRecentsAttachedState(false);
+ maybeUpdateRecentsAttachedState(false, new ActiveGestureLog.CompoundString(
+ "on settled on end target (animate=false)"));
final GestureEndTarget endTarget = mGestureState.getEndTarget();
// Wait until the given View (if supplied) draws before resuming the last task.
View postResumeLastTask = mActivityInterface.onSettledOnEndTarget(endTarget);
@@ -1288,7 +1338,8 @@
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- maybeUpdateRecentsAttachedState();
+ maybeUpdateRecentsAttachedState(new ActiveGestureLog.CompoundString(
+ "animate to progress internal"));
// If we are transitioning to launcher, then listen for the activity to be restarted while
// the transition is in progress
@@ -1607,7 +1658,9 @@
mRecentsView.post(mRecentsView::resetTaskVisuals);
}
// Make sure recents is in its final state
- maybeUpdateRecentsAttachedState(false);
+ maybeUpdateRecentsAttachedState(
+ false, new ActiveGestureLog.CompoundString(
+ "setting up window animation (animate=false)"));
mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
}
});
@@ -1829,6 +1882,7 @@
}
private void finishCurrentTransitionToRecents() {
+ // TODO(b/245569277#comment2): enable once isFreeformActive is implemented
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
if (mRecentsAnimationController != null) {
mRecentsAnimationController.detachNavigationBarFromApp(true);
@@ -2034,16 +2088,59 @@
public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
if (handleTaskAppeared(appearedTaskTargets)) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ false,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
+ Optional<RemoteAnimationTargetCompat> taskTargetOptional =
+ Arrays.stream(appearedTaskTargets)
+ .filter(targetCompat ->
+ targetCompat.taskId == mGestureState.getLastStartedTaskId())
+ .findFirst();
+ if (!taskTargetOptional.isPresent()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+ RemoteAnimationTargetCompat taskTarget = taskTargetOptional.get();
+ TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
+ if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+
+ ViewGroup splashView = mActivity.getDragLayer();
+
+ // When revealing the app with launcher splash screen, make the app visible
+ // and behind the splash view before the splash is animated away.
+ SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+ new SyncRtSurfaceTransactionApplierCompat(splashView);
+ ArrayList<SurfaceParams> params = new ArrayList<>();
+ for (RemoteAnimationTargetCompat target : appearedTaskTargets) {
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ builder.withAlpha(1);
+ builder.withLayer(-1);
+ params.add(builder.build());
+ }
+ surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[0]));
+
+ SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash,
+ mSplashMainWindowShiftLength, new TransactionPool(), new Rect(),
+ SPLASH_ANIMATION_DURATION, SPLASH_FADE_OUT_DURATION,
+ /* iconStartAlpha= */ 0, /* brandingStartAlpha= */ 0,
+ SPLASH_APP_REVEAL_DELAY, SPLASH_APP_REVEAL_DURATION,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishRecentsAnimationOnTasksAppeared();
+ }
+ });
}
}
}
+ private void finishRecentsAnimationOnTasksAppeared() {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false /* toRecents */, null /* onFinishComplete */);
+ }
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+
/**
* @return The index of the TaskView in RecentsView whose taskId matches the task that will
* resume if we finish the controller.
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 226b173..a343c2d 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -57,6 +57,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
@@ -393,14 +394,16 @@
public interface AnimationFactory {
- void createActivityInterface(long transitionLength);
+ void createActivityInterface(long transitionLength, ActiveGestureLog.CompoundString reason);
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
+ * @param reason Explanation for why this method is being called with the given param values
*/
- default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
+ default void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, ActiveGestureLog.CompoundString reason) { }
default boolean isRecentsAttachedToAppWindow() {
return false;
@@ -442,7 +445,8 @@
}
@Override
- public void createActivityInterface(long transitionLength) {
+ public void createActivityInterface(
+ long transitionLength, ActiveGestureLog.CompoundString reason) {
PendingAnimation pa = new PendingAnimation(transitionLength * 2);
createBackgroundToOverviewAnim(mActivity, pa);
AnimatorPlaybackController controller = pa.createPlaybackController();
@@ -465,13 +469,29 @@
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
if (DisplayController.getNavigationMode(mActivity) == NavigationMode.NO_BUTTON) {
- setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
+ setRecentsAttachedToAppWindow(
+ mIsAttachedToWindow,
+ false,
+ reason.append("; reapplying the attached state (attached=")
+ .append(Boolean.toString(mIsAttachedToWindow))
+ .append(", animate=false)"));
}
}
@Override
- public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
+ public void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, ActiveGestureLog.CompoundString reason) {
+ // TODO(b/244593270): remove these logs; too verbose
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("setRecentsAttachedToAppWindow: attached=")
+ .append(Boolean.toString(attached))
+ .append(", animate=")
+ .append(Boolean.toString(animate))
+ .append(", reason=")
+ .append(reason));
if (mIsAttachedToWindow == attached && animate) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: exiting early"));
return;
}
mIsAttachedToWindow = attached;
@@ -488,9 +508,21 @@
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
if (!recentsView.isShown() && animate) {
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, fromTranslation);
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: recents view not shown, setting ")
+ .append("ADJACENT_PAGE_HORIZONTAL_OFFSET to ")
+ .append(Float.toString(fromTranslation)));
} else {
fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(recentsView);
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: updating fromTranslation to ")
+ .append(Float.toString(fromTranslation)));
}
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: fromTranslation=")
+ .append(Float.toString(fromTranslation))
+ .append(", toTranslation=")
+ .append(Float.toString(toTranslation)));
if (!animate) {
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, toTranslation);
} else {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 6b616b1..7bcc661 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -17,6 +17,8 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
+import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -30,6 +32,7 @@
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.recents.IRecentTasksListener;
@@ -253,8 +256,9 @@
};
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
+
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (rawTask.getType() == GroupedRecentTaskInfo.TYPE_FREEFORM) {
+ if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
GroupTask desktopTask = createDesktopTask(rawTask);
allTasks.add(desktopTask);
continue;
@@ -284,14 +288,18 @@
return allTasks;
}
- private GroupTask createDesktopTask(GroupedRecentTaskInfo taskInfo) {
- // TODO(b/244348395): create a subclass of GroupTask for desktop tile
- // We need a single task information as the primary task. Use the first task
- Task.TaskKey key = new Task.TaskKey(taskInfo.getTaskInfo1());
- Task task = new Task(key);
- task.desktopTile = true;
- task.topActivity = key.sourceComponent;
- return new GroupTask(task, null, null);
+ private DesktopTask createDesktopTask(GroupedRecentTaskInfo recentTaskInfo) {
+ ArrayList<Task> tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
+ for (ActivityManager.RecentTaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
+ Task.TaskKey key = new Task.TaskKey(taskInfo);
+ Task task = Task.from(key, taskInfo, false);
+ task.setLastSnapshotData(taskInfo);
+ task.positionInParent = taskInfo.positionInParent;
+ task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
+ // TODO(b/244348395): tasks should be sorted from oldest to most recently used
+ tasks.add(task);
+ }
+ return new DesktopTask(tasks);
}
private SplitConfigurationOptions.SplitBounds convertSplitBounds(
@@ -306,7 +314,7 @@
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
ArrayList<GroupTask> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
- newTasks.add(new GroupTask(tasks.get(i)));
+ newTasks.add(tasks.get(i).copy());
}
return newTasks;
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 7d3d8a2..df80e2f 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -302,9 +302,15 @@
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
// Mt K(0)` K(t) Mt`
TaskThumbnailView[] thumbnails = v.getThumbnails();
- Matrix[] mt = new Matrix[simulatorCopies.length];
- Matrix[] mti = new Matrix[simulatorCopies.length];
- for (int i = 0; i < thumbnails.length; i++) {
+
+ // In case simulator copies and thumbnail size do no match, ensure we get the lesser.
+ // This ensures we do not create arrays with empty elements or attempt to references
+ // indexes out of array bounds.
+ final int matrixSize = Math.min(simulatorCopies.length, thumbnails.length);
+
+ Matrix[] mt = new Matrix[matrixSize];
+ Matrix[] mti = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
TaskThumbnailView ttv = thumbnails[i];
RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
@@ -321,14 +327,14 @@
mti[i] = localMti;
}
- Matrix[] k0i = new Matrix[simulatorCopies.length];
- for (int i = 0; i < simulatorCopies.length; i++) {
+ Matrix[] k0i = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
k0i[i] = new Matrix();
simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
}
Matrix animationMatrix = new Matrix();
out.addOnFrameCallback(() -> {
- for (int i = 0; i < simulatorCopies.length; i++) {
+ for (int i = 0; i < matrixSize; i++) {
animationMatrix.set(mt[i]);
animationMatrix.postConcat(k0i[i]);
animationMatrix.postConcat(simulatorCopies[i]
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 37a28e5..a9ff0fb 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -190,7 +190,8 @@
getCardinality(info), // cardinality = 16;
info.getWidget().getSpanX(), // span_x = 17 [default = 1];
info.getWidget().getSpanY(), // span_y = 18 [default = 1];
- getAttributes(info) /* attributes */
+ getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
+ false /* is_kids_mode = 20 */
);
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 143042f..2a513ee 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -16,12 +16,14 @@
package com.android.quickstep.util;
import android.annotation.CallSuper;
+import android.view.Surface.Rotation;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.util.HashMap;
import java.util.Map;
@@ -32,15 +34,20 @@
public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+ private final RotationChangeProvider mRotationChangeProvider;
private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
+ private final UnfoldMoveFromCenterRotationListener mRotationListener =
+ new UnfoldMoveFromCenterRotationListener();
private boolean mAnimationInProgress = false;
- public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
+ public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
+ mRotationChangeProvider = rotationChangeProvider;
}
@CallSuper
@@ -50,6 +57,7 @@
mMoveFromCenterAnimation.updateDisplayProperties();
onPrepareViewsForAnimation();
onTransitionProgress(0f);
+ mRotationChangeProvider.addCallback(mRotationListener);
}
@CallSuper
@@ -62,6 +70,7 @@
@Override
public void onTransitionFinished() {
mAnimationInProgress = false;
+ mRotationChangeProvider.removeCallback(mRotationListener);
mMoveFromCenterAnimation.onTransitionFinished();
clearRegisteredViews();
}
@@ -109,4 +118,14 @@
view.setClipChildren(originalClipChildren);
}
}
+
+ private class UnfoldMoveFromCenterRotationListener implements
+ RotationChangeProvider.RotationListener {
+
+ @Override
+ public void onRotationChanged(@Rotation int newRotation) {
+ mMoveFromCenterAnimation.updateDisplayProperties(newRotation);
+ updateRegisteredViewsIfNeeded();
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
new file mode 100644
index 0000000..433d23f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * A {@link Task} container that can contain N number of tasks that are part of the desktop in
+ * recent tasks list.
+ */
+public class DesktopTask extends GroupTask {
+
+ public ArrayList<Task> tasks;
+
+ public DesktopTask(ArrayList<Task> tasks) {
+ super(tasks.get(0), null, null, TaskView.Type.DESKTOP);
+ this.tasks = tasks;
+ }
+
+ @Override
+ public boolean containsTask(int taskId) {
+ for (Task task : tasks) {
+ if (task.key.id == taskId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasMultipleTasks() {
+ return true;
+ }
+
+ @Override
+ public DesktopTask copy() {
+ return new DesktopTask(tasks);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index f30d00c..2be4f0a 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -20,6 +20,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
/**
@@ -27,24 +28,25 @@
* are represented as an app-pair in the recents task list.
*/
public class GroupTask {
- public @NonNull Task task1;
- public @Nullable Task task2;
- public @Nullable
- SplitBounds mSplitBounds;
+ @NonNull
+ public final Task task1;
+ @Nullable
+ public final Task task2;
+ @Nullable
+ public final SplitBounds mSplitBounds;
+ @TaskView.Type
+ public final int taskViewType;
- public GroupTask(@NonNull Task t1, @Nullable Task t2,
- @Nullable SplitBounds splitBounds) {
+ public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
+ this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE);
+ }
+
+ protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds,
+ @TaskView.Type int taskViewType) {
task1 = t1;
task2 = t2;
mSplitBounds = splitBounds;
- }
-
- public GroupTask(@NonNull GroupTask group) {
- task1 = new Task(group.task1);
- task2 = group.task2 != null
- ? new Task(group.task2)
- : null;
- mSplitBounds = group.mSplitBounds;
+ this.taskViewType = taskViewType;
}
public boolean containsTask(int taskId) {
@@ -54,4 +56,14 @@
public boolean hasMultipleTasks() {
return task2 != null;
}
+
+ /**
+ * Create a copy of this instance
+ */
+ public GroupTask copy() {
+ return new GroupTask(
+ new Task(task1),
+ task2 != null ? new Task(task2) : null,
+ mSplitBounds);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 97be437..f12074b 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -24,7 +24,6 @@
import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import androidx.core.view.OneShotPreDrawListener;
@@ -34,6 +33,7 @@
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -62,16 +62,17 @@
public LauncherUnfoldAnimationController(
Launcher launcher,
WindowManager windowManager,
- UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
+ UnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
+ RotationChangeProvider rotationChangeProvider) {
mLauncher = launcher;
mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
unfoldTransitionProgressProvider);
mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
- WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
+ rotationChangeProvider, mProgressProvider);
mNaturalOrientationProgressProvider.init();
// Animated in all orientations
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index eec8582..ad54a70 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -46,6 +46,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -122,8 +123,7 @@
if (grid.isVerticalBarLayout()) {
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i);
- CellLayout.LayoutParams lp =
- ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
}
} else {
@@ -193,7 +193,7 @@
// Set up springs on workspace items.
for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
View child = itemsContainer.getChildAt(i);
- CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index dc97dd6..01a997a 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.Launcher;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves hotseat icons from center to the sides (final position)
@@ -29,8 +30,9 @@
private final Launcher mLauncher;
- public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager,
+ 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 354d157..95a4b8f 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.Launcher;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves launcher icons and widgets from center to the sides (final position)
@@ -30,8 +31,9 @@
private final Launcher mLauncher;
- public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
new file mode 100644
index 0000000..9874f96
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+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.
+public class DesktopTaskView extends TaskView {
+
+ /** Flag to indicate whether desktop mode is available on the device */
+ public static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode", false);
+
+ private static final String TAG = DesktopTaskView.class.getSimpleName();
+
+ private static final boolean DEBUG = true;
+
+ private List<Task> mTasks;
+
+ private final ArrayList<TaskThumbnailView> mSnapshotViews = new ArrayList<>();
+
+ /** Maps {@code taskIds} to corresponding {@link TaskThumbnailView}s */
+ private final SparseArray<TaskThumbnailView> mSnapshotViewMap = new SparseArray<>();
+
+ private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
+
+ public DesktopTaskView(Context context) {
+ this(context, null);
+ }
+
+ public DesktopTaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ float[] outerRadii = new float[8];
+ Arrays.fill(outerRadii, getTaskCornerRadius());
+ RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
+ ShapeDrawable background = new ShapeDrawable(shape);
+ background.setTint(getResources().getColor(android.R.color.system_neutral2_300));
+ // TODO(b/244348395): this should be wallpaper
+ setBackground(background);
+
+ mSnapshotViews.add(mSnapshotView);
+ }
+
+ @Override
+ public void bind(Task task, RecentsOrientedState orientedState) {
+ bind(Collections.singletonList(task), orientedState);
+ }
+
+ /**
+ * Updates this desktop task to the gives task list defined in {@code tasks}
+ */
+ public void bind(List<Task> tasks, RecentsOrientedState orientedState) {
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("bind tasks=").append(tasks.size()).append("\n");
+ for (Task task : tasks) {
+ sb.append(" key=").append(task.key).append("\n");
+ }
+ Log.d(TAG, sb.toString());
+ }
+ if (tasks.isEmpty()) {
+ return;
+ }
+ cancelPendingLoadTasks();
+
+ mTasks = tasks;
+ mSnapshotViewMap.clear();
+
+ // Ensure there are equal number of snapshot views and tasks.
+ // More tasks than views, add views. More views than tasks, remove views.
+ // TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews
+ if (mSnapshotViews.size() > mTasks.size()) {
+ int diff = mSnapshotViews.size() - mTasks.size();
+ for (int i = 0; i < diff; i++) {
+ TaskThumbnailView snapshotView = mSnapshotViews.remove(0);
+ removeView(snapshotView);
+ }
+ } else if (mSnapshotViews.size() < mTasks.size()) {
+ int diff = mTasks.size() - mSnapshotViews.size();
+ for (int i = 0; i < diff; i++) {
+ TaskThumbnailView snapshotView = new TaskThumbnailView(getContext());
+ mSnapshotViews.add(snapshotView);
+ addView(snapshotView, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ }
+ }
+
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ TaskThumbnailView snapshotView = mSnapshotViews.get(i);
+ snapshotView.bind(task);
+ mSnapshotViewMap.put(task.key.id, snapshotView);
+ }
+
+ updateTaskIdContainer();
+ updateTaskIdAttributeContainer();
+
+ setOrientationState(orientedState);
+ }
+
+ private void updateTaskIdContainer() {
+ // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
+ // At least 2 elements in the array
+ mTaskIdContainer = new int[Math.max(mTasks.size(), 2)];
+ for (int i = 0; i < mTasks.size(); i++) {
+ mTaskIdContainer[i] = mTasks.get(i).key.id;
+ }
+ }
+
+ private void updateTaskIdAttributeContainer() {
+ // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
+ // At least 2 elements in the array
+ mTaskIdAttributeContainer = new TaskIdAttributeContainer[Math.max(mTasks.size(), 2)];
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ mTaskIdAttributeContainer[i] = createAttributeContainer(task, thumbnailView);
+ }
+ }
+
+ private TaskIdAttributeContainer createAttributeContainer(Task task,
+ TaskThumbnailView thumbnailView) {
+ return new TaskIdAttributeContainer(task, thumbnailView, null, STAGE_POSITION_UNDEFINED);
+ }
+
+ @Nullable
+ @Override
+ public Task getTask() {
+ // TODO(b/249371338): returning first task. This won't work well with multiple tasks.
+ return mTasks.size() > 0 ? mTasks.get(0) : null;
+ }
+
+ @Override
+ public TaskThumbnailView getThumbnail() {
+ // TODO(b/249371338): returning single thumbnail. This won't work well with multiple tasks.
+ Task task = getTask();
+ if (task != null) {
+ return mSnapshotViewMap.get(task.key.id);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsTaskId(int taskId) {
+ // Thumbnail map contains taskId -> thumbnail map. Use the keys for contains
+ return mSnapshotViewMap.contains(taskId);
+ }
+
+ @Override
+ public void onTaskListVisibilityChanged(boolean visible, int changes) {
+ cancelPendingLoadTasks();
+ if (visible) {
+ RecentsModel model = RecentsModel.INSTANCE.get(getContext());
+ TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
+
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ for (Task task : mTasks) {
+ CancellableTask<?> thumbLoadRequest =
+ thumbnailCache.updateThumbnailInBackground(task, thumbnailData -> {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(task, thumbnailData);
+ }
+ });
+ if (thumbLoadRequest != null) {
+ mPendingThumbnailRequests.add(thumbLoadRequest);
+ }
+ }
+ }
+ } else {
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ for (Task task : mTasks) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(null, null);
+ }
+ // Reset the task thumbnail ref
+ task.thumbnail = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setOrientationState(RecentsOrientedState orientationState) {
+ // TODO(b/249371338): this copies logic from TaskView
+ PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
+ boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+
+ LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
+
+ int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
+ int taskMargin = deviceProfile.overviewTaskMarginPx;
+
+ orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
+ thumbnailTopMargin, isRtl);
+
+ LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
+ snapshotParams.topMargin = thumbnailTopMargin;
+
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
+ thumbnailView.setLayoutParams(snapshotParams);
+ }
+ }
+
+ @Override
+ protected void cancelPendingLoadTasks() {
+ for (CancellableTask<?> cancellableTask : mPendingThumbnailRequests) {
+ cancellableTask.cancel();
+ }
+ mPendingThumbnailRequests.clear();
+ }
+
+ @Override
+ public boolean offerTouchToChildren(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ protected boolean showTaskMenuWithContainer(IconView iconView) {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public RunnableList launchTaskAnimated() {
+ RunnableList endCallback = new RunnableList();
+ SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ RecentsView<?, ?> recentsView = getRecentsView();
+ recentsView.addSideTaskLaunchCallback(endCallback);
+ return endCallback;
+ }
+
+ @Override
+ public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+ SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ callback.accept(true);
+ }
+
+ @Override
+ void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ // Sets new thumbnails based on the incoming data and refreshes the rest.
+ // Create a copy of the thumbnail map, so we can track thumbnails that need refreshing.
+ SparseArray<TaskThumbnailView> thumbnailsToRefresh = mSnapshotViewMap.clone();
+ if (thumbnailDatas != null) {
+ for (Task task : mTasks) {
+ int key = task.key.id;
+ TaskThumbnailView thumbnailView = thumbnailsToRefresh.get(key);
+ ThumbnailData thumbnailData = thumbnailDatas.get(key);
+ if (thumbnailView != null && thumbnailData != null) {
+ thumbnailView.setThumbnail(task, thumbnailData);
+ // Remove this thumbnail from the list that should be refreshed.
+ thumbnailsToRefresh.remove(key);
+ }
+ }
+ }
+
+ // Refresh the rest that were not updated.
+ for (int i = 0; i < thumbnailsToRefresh.size(); i++) {
+ thumbnailsToRefresh.valueAt(i).refresh();
+ }
+ }
+
+ @Override
+ public TaskThumbnailView[] getThumbnails() {
+ TaskThumbnailView[] thumbnails = new TaskThumbnailView[mSnapshotViewMap.size()];
+ for (int i = 0; i < thumbnails.length; i++) {
+ thumbnails[i] = mSnapshotViewMap.valueAt(i);
+ }
+ return thumbnails;
+ }
+
+ @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)
+ for (Task task : mTasks) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(task, null);
+ }
+ }
+ setOverlayEnabled(false);
+ onTaskListVisibilityChanged(false);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int containerWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int containerHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(containerWidth, containerHeight);
+
+ int thumbnails = mSnapshotViewMap.size();
+ if (thumbnails == 0) {
+ return;
+ }
+
+ int windowWidth = mActivity.getDeviceProfile().widthPx;
+ int windowHeight = mActivity.getDeviceProfile().heightPx;
+
+ float scaleWidth = containerWidth / (float) windowWidth;
+ float scaleHeight = containerHeight / (float) windowHeight;
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "onMeasure: container=[" + containerWidth + "," + containerHeight + "] window=["
+ + windowWidth + "," + windowHeight + "] scale=[" + scaleWidth + ","
+ + scaleHeight + "]");
+ }
+
+ // Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ Rect taskSize = task.appBounds;
+ if (taskSize == null) {
+ // Default to quarter of the desktop if we did not get app bounds.
+ taskSize = new Rect(0, 0, windowWidth / 4, windowHeight / 4);
+ }
+
+ int thumbWidth = (int) (taskSize.width() * scaleWidth);
+ int thumbHeight = (int) (taskSize.height() * scaleHeight);
+
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.measure(MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY));
+
+ // Position the task to the same position as it would be on the desktop
+ Point positionInParent = task.positionInParent;
+ if (positionInParent == null) {
+ positionInParent = new Point(0, 0);
+ }
+ int taskX = (int) (positionInParent.x * scaleWidth);
+ int taskY = (int) (positionInParent.y * scaleHeight);
+ thumbnailView.setX(taskX);
+ thumbnailView.setY(taskY);
+
+ if (DEBUG) {
+ Log.d(TAG, "onMeasure: task=" + task.key + " thumb=[" + thumbWidth + ","
+ + thumbHeight + "]" + " pos=[" + taskX + "," + taskY + "]");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setOverlayEnabled(boolean overlayEnabled) {
+ // Intentional no-op to prevent setting smart actions overlay on thumbnails
+ }
+
+ @Override
+ public void setFullscreenProgress(float progress) {
+ // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
+ progress = Utilities.boundToRange(progress, 0, 1);
+ mFullscreenProgress = progress;
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
+ thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
+ updateSnapshotRadius();
+ }
+ }
+
+ @Override
+ protected void updateSnapshotRadius() {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams);
+ }
+ }
+
+ @Override
+ protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ // no-op
+ }
+
+ @Override
+ public void setColorTint(float amount, int tintColor) {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setDimAlpha(amount);
+ }
+ }
+
+ @Override
+ protected void applyThumbnailSplashAlpha() {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setSplashAlpha(mTaskThumbnailSplashAlpha);
+ }
+ }
+
+ @Override
+ void setThumbnailVisibility(int visibility) {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setVisibility(visibility);
+ }
+ }
+
+ @Override
+ protected boolean confirmSecondSplitSelectApp() {
+ // Desktop tile can't be in split screen
+ return false;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 3a5f606..71b0c60 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -225,6 +225,12 @@
}
@Override
+ public boolean containsTaskId(int taskId) {
+ return (mTask != null && mTask.key.id == taskId)
+ || (mSecondaryTask != null && mSecondaryTask.key.id == taskId);
+ }
+
+ @Override
public TaskThumbnailView[] getThumbnails() {
return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0e0acf0..c8ef958 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -54,6 +54,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
@@ -72,6 +73,7 @@
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.LocusId;
import android.content.res.Configuration;
@@ -171,6 +173,7 @@
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
@@ -195,6 +198,7 @@
import com.android.wm.shell.pip.IPipAnimationListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -477,10 +481,11 @@
private final InvariantDeviceProfile mIdp;
/**
- * Getting views should be done via {@link #getTaskViewFromPool(boolean)}
+ * Getting views should be done via {@link #getTaskViewFromPool(int)}
*/
private final ViewPool<TaskView> mTaskViewPool;
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
+ private final ViewPool<DesktopTaskView> mDesktopTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
@@ -737,6 +742,8 @@
10 /* initial size */);
mGroupedTaskViewPool = new ViewPool<>(context, this,
R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
+ mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
+ 5 /* max size */, 1 /* initial size */);
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@@ -981,6 +988,8 @@
}
if (child instanceof GroupedTaskView) {
mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
+ } else if (child instanceof DesktopTaskView) {
+ mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
} else {
mTaskViewPool.recycle(taskView);
}
@@ -1199,8 +1208,7 @@
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
- int[] taskIds = taskView.getTaskIds();
- if (taskIds[0] == taskId || taskIds[1] == taskId) {
+ if (taskView.containsTaskId(taskId)) {
return taskView;
}
}
@@ -1481,40 +1489,28 @@
// Add views as children based on whether it's grouped or single task
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean hasMultipleTasks = groupTask.hasMultipleTasks();
- TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
+ TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);
addView(taskView);
- if (hasMultipleTasks) {
+ 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);
+ } else if (taskView instanceof DesktopTaskView) {
+ ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
+ mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
}
+
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
}
- boolean settlingOnNewTask = mNextPage != INVALID_PAGE;
- if (settlingOnNewTask) {
- // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
- mCurrentPage = previousCurrentPage;
- } else {
- // TODO(b/238461210): Remove logging once root cause of flake detected.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d("b/238461210", "RecentsView#applyLoadPlan() -> !settlingOnNewTask -> "
- + "previousCurrentPage: " + previousCurrentPage
- + ", getScrollForPage(previousCurrentPage): "
- + getScrollForPage(previousCurrentPage));
- }
- setCurrentPage(previousCurrentPage);
- }
-
// Keep same previous focused task
TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
// If the list changed, maybe the focused task doesn't exist anymore
@@ -1539,21 +1535,36 @@
}
int targetPage = -1;
- if (!settlingOnNewTask) {
+ if (mNextPage != INVALID_PAGE) {
+ // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
+ mCurrentPage = previousCurrentPage;
+ if (currentTaskId != -1) {
+ currentTaskView = getTaskViewByTaskId(currentTaskId);
+ if (currentTaskView != null) {
+ targetPage = indexOfChild(currentTaskView);
+ }
+ }
+ } else {
// Set the current page to the running task, but not if settling on new task.
if (runningTaskId != -1) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
targetPage = indexOfChild(requireTaskViewAt(0));
}
- } else if (currentTaskId != -1) {
- currentTaskView = getTaskViewByTaskId(currentTaskId);
- if (currentTaskView != null) {
- targetPage = indexOfChild(currentTaskView);
- }
}
if (targetPage != -1 && mCurrentPage != targetPage) {
- setCurrentPage(targetPage);
+ int finalTargetPage = targetPage;
+ runOnPageScrollsInitialized(() -> {
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d("b/246283207", "RecentsView#applyLoadPlan() -> "
+ + "previousCurrentPage: " + previousCurrentPage
+ + ", targetPage: " + finalTargetPage
+ + ", getScrollForPage(targetPage): "
+ + getScrollForPage(finalTargetPage));
+ }
+ setCurrentPage(finalTargetPage);
+ });
}
if (mIgnoreResetTaskId != -1 &&
@@ -2123,10 +2134,19 @@
* Handle the edge case where Recents could increment task count very high over long
* period of device usage. Probably will never happen, but meh.
*/
- private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
- T taskView = isGrouped ?
- (T) mGroupedTaskViewPool.getView() :
- (T) mTaskViewPool.getView();
+ private TaskView getTaskViewFromPool(@TaskView.Type int type) {
+ TaskView taskView;
+ switch (type) {
+ case TaskView.Type.GROUPED:
+ taskView = mGroupedTaskViewPool.getView();
+ break;
+ case TaskView.Type.DESKTOP:
+ taskView = mDesktopTaskViewPool.getView();
+ break;
+ case TaskView.Type.SINGLE:
+ default:
+ taskView = mTaskViewPool.getView();
+ }
taskView.setTaskViewId(mTaskViewIdCount);
if (mTaskViewIdCount == Integer.MAX_VALUE) {
mTaskViewIdCount = 0;
@@ -2318,12 +2338,19 @@
}
int runningTaskViewId = -1;
boolean needGroupTaskView = runningTasks.length > 1;
+ boolean needDesktopTask = hasDesktopTask(runningTasks);
if (shouldAddStubTaskView(runningTasks)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
- if (needGroupTaskView) {
- taskView = getTaskViewFromPool(true);
+ if (needDesktopTask) {
+ taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
+ mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
+ addView(taskView, 0);
+ ((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
+ mOrientationState);
+ } 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
@@ -2332,7 +2359,7 @@
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
mOrientationState, mSplitBoundsConfig);
} else {
- taskView = getTaskViewFromPool(false);
+ 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
@@ -2367,6 +2394,18 @@
reloadIfNeeded();
}
+ private boolean hasDesktopTask(Task[] runningTasks) {
+ if (!DESKTOP_MODE_SUPPORTED) {
+ return false;
+ }
+ for (Task task : runningTasks) {
+ if (task.key.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Sets the running task id, cleaning up the old running task if necessary.
*/
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index fae96dc..b0b111d 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -121,7 +121,7 @@
int navButtonWidth = getResources().getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_size);
int extraMargin = getResources().getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_margin);
+ R.dimen.taskbar_split_instructions_margin);
// Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
// up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
// center of the screen and we want to center it in the remaining space, therefore we
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 04a0af1..6792dc5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -370,7 +370,7 @@
* <p>We want to show the splash if the aspect ratio or rotation of the thumbnail would be
* different from the task.
*/
- boolean shouldShowSplashView() {
+ public boolean shouldShowSplashView() {
return isThumbnailAspectRatioDifferentFromThumbnailData()
|| isThumbnailRotationDifferentFromTask();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 0125775..35f0f5d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,7 +18,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
-import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
@@ -87,7 +86,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
@@ -136,6 +134,17 @@
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
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;
@@ -332,7 +341,7 @@
protected TaskThumbnailView mSnapshotView;
protected IconView mIconView;
protected final DigitalWellBeingToast mDigitalWellBeingToast;
- private float mFullscreenProgress;
+ protected float mFullscreenProgress;
private float mGridProgress;
protected float mTaskThumbnailSplashAlpha;
private float mNonGridScale = 1;
@@ -374,8 +383,8 @@
/**
* Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
*/
- protected final int[] mTaskIdContainer = new int[]{-1, -1};
- protected final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
+ protected int[] mTaskIdContainer = new int[]{-1, -1};
+ protected TaskIdAttributeContainer[] mTaskIdAttributeContainer =
new TaskIdAttributeContainer[2];
private boolean mShowScreenshot;
@@ -526,6 +535,13 @@
}
/**
+ * Check if given {@code taskId} is tracked in this view
+ */
+ public boolean containsTaskId(int taskId) {
+ return mTask != null && mTask.key.id == taskId;
+ }
+
+ /**
* @return integer array of two elements to be size consistent with max number of tasks possible
* index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
*/
@@ -565,13 +581,13 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
RecentsView recentsView = getRecentsView();
- if (recentsView == null || mTask == null) {
+ if (recentsView == null || getTask() == null) {
return false;
}
SplitSelectStateController splitSelectStateController =
recentsView.getSplitSelectController();
if (splitSelectStateController.isSplitSelectActive() &&
- splitSelectStateController.getInitialTaskId() == mTask.key.id) {
+ splitSelectStateController.getInitialTaskId() == getTask().key.id) {
// Prevent taps on the this taskview if it's being animated into split select state
return false;
}
@@ -598,11 +614,14 @@
* @return {@code true} if user is already in split select mode and this tap was to choose the
* second app. {@code false} otherwise
*/
- private boolean confirmSecondSplitSelectApp() {
+ protected boolean confirmSecondSplitSelectApp() {
int index = getLastSelectedChildTaskIndex();
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
- return getRecentsView().confirmSplitSelect(this, container.getTask(),
- container.getIconView(), container.getThumbnailView());
+ if (container != null) {
+ return getRecentsView().confirmSplitSelect(this, container.getTask(),
+ container.getIconView(), container.getThumbnailView());
+ }
+ return false;
}
/**
@@ -669,9 +688,7 @@
if (freezeTaskList) {
opts.setFreezeRecentTasksReordering();
}
- // TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow.
- opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView()
- ? SPLASH_SCREEN_STYLE_SOLID_COLOR : opts.getSplashScreenStyle());
+ opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView());
Task.TaskKey key = mTask.key;
UI_HELPER_EXECUTOR.execute(() -> {
if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
@@ -710,11 +727,6 @@
RecentsView recentsView = getRecentsView();
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
RunnableList runnableList = new RunnableList();
- if (mTask != null && mTask.desktopTile) {
- // clicked on desktop
- SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
- return runnableList;
- }
if (isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
return runnableList;
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 4dee6e7..39f93d9 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -34,8 +34,7 @@
android:clipChildren="false"
android:orientation="horizontal"
android:paddingLeft="12dp"
- android:paddingRight="12dp"
- android:alpha="0">
+ android:paddingRight="12dp">
<com.android.launcher3.folder.FolderNameEditText
android:id="@+id/folder_name"
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 0a4943e..67998d5 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -21,9 +21,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="work_folder_name" msgid="3753320833950115786">"কৰ্মস্থান"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"এপটো ইনষ্টল কৰা নহ\'ল।"</string>
- <string name="activity_not_available" msgid="7456344436509528827">"এপটো নাই"</string>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপটোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"এপ্টো ইনষ্টল কৰা নহ\'ল।"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"এপ্টো নাই"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপ্টোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"ৱিজেটবোৰক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"শ্বৰ্টকাট নাই"</string>
<string name="home_screen" msgid="5629429142036709174">"গৃহ স্ক্ৰীন"</string>
@@ -119,8 +119,8 @@
<string name="package_state_unknown" msgid="7592128424511031410">"অজ্ঞাত"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"আঁতৰাওক"</string>
<string name="abandoned_search" msgid="891119232568284442">"সন্ধান কৰক"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপটো ইনষ্টল কৰা হোৱা নাই"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপটো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপটো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপ্টো ইনষ্টল কৰা হোৱা নাই"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপ্টো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপ্টো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হৈছে"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 3cec97d..69157bc 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -54,7 +54,7 @@
<string name="widget_education_header" msgid="4874760613775913787">"အသုံးဝင်သော အချက်အလက်များကို အလွယ်တကူ ရယူလိုက်ပါ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"အက်ပ်မဖွင့်ဘဲ အချက်အလက်များရယူရန် ပင်မစာမျက်နှာတွင် ဝိဂျက်များ ထည့်နိုင်သည်"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ဝိဂျက် ဆက်တင်များကို ပြောင်းရန် တို့ပါ"</string>
- <string name="widget_education_close_button" msgid="8676165703104836580">"ရပြီ"</string>
+ <string name="widget_education_close_button" msgid="8676165703104836580">"နားလည်ပြီ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ဝိဂျက် ဆက်တင်များကို ပြောင်းပါ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ရှာဖွေမှု အက်ပ်များ"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"အက်ပ်များကို ဖွင့်နေသည်…"</string>
@@ -166,7 +166,7 @@
<string name="work_apps_paused_body" msgid="261634750995824906">"သင်၏ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"အလုပ်သုံးအက်ပ်များ ပိတ်ထားသည်။ သင်၏ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"အလုပ်သုံးအက်ပ်များကို တံဆိပ်တပ်ထားပြီး သင်၏ IT စီမံခန့်ခွဲသူက မြင်နိုင်ပါသည်"</string>
- <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ရပြီ"</string>
+ <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"နားလည်ပြီ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"အလုပ်သုံးအက်ပ်များကို ခဏရပ်ရန်"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"အလုပ်သုံးအက်ပ်များ ဖွင့်ရန်"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index fbb0a57..555fbb4 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -32,6 +32,7 @@
import androidx.annotation.Px;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
@@ -265,7 +266,7 @@
}
}
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams();
ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
lp.cellX = lp.tmpCellX = widgetInfo.cellX;
lp.cellY = lp.tmpCellY = widgetInfo.cellY;
@@ -405,7 +406,7 @@
*/
private void resizeWidgetIfNeeded(boolean onDismiss) {
ViewGroup.LayoutParams wlp = mWidgetView.getLayoutParams();
- if (!(wlp instanceof CellLayout.LayoutParams)) {
+ if (!(wlp instanceof CellLayoutLayoutParams)) {
return;
}
DeviceProfile dp = mLauncher.getDeviceProfile();
@@ -420,7 +421,7 @@
mDirectionVector[0] = 0;
mDirectionVector[1] = 0;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) wlp;
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) wlp;
int spanX = lp.cellHSpan;
int spanY = lp.cellVSpan;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5fb8925..c0a00c2 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -942,6 +942,11 @@
return mIconSize;
}
+ public boolean isDisplaySearchResult() {
+ return mDisplay == DISPLAY_SEARCH_RESULT ||
+ mDisplay == DISPLAY_SEARCH_RESULT_SMALL;
+ }
+
private void updateTranslation() {
super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ mTranslationForMoveFromCenterAnimation.x
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9f3e1fa..c1df403 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -61,6 +61,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -130,7 +131,7 @@
// These arrays are used to implement the drag visualization on x-large screens.
// They are used as circular arrays, indexed by mDragOutlineCurrent.
- @Thunk final CellLayout.LayoutParams[] mDragOutlines = new CellLayout.LayoutParams[4];
+ @Thunk final CellLayoutLayoutParams[] mDragOutlines = new CellLayoutLayoutParams[4];
@Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
private final InterruptibleInOutAnimator[] mDragOutlineAnims =
new InterruptibleInOutAnimator[mDragOutlines.length];
@@ -139,7 +140,7 @@
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
- @Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
+ @Thunk final ArrayMap<CellLayoutLayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
@Thunk final ArrayMap<Reorderable, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
private boolean mItemPlacementDirty = false;
@@ -269,7 +270,7 @@
mDragCell[0] = mDragCell[1] = -1;
mDragCellSpan[0] = mDragCellSpan[1] = -1;
for (int i = 0; i < mDragOutlines.length; i++) {
- mDragOutlines[i] = new CellLayout.LayoutParams(0, 0, 0, 0);
+ mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0);
}
mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
@@ -737,9 +738,19 @@
return mContainerType == WORKSPACE;
}
- public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
- boolean markCells) {
- final LayoutParams lp = params;
+ /**
+ * Adds the given view to the CellLayout
+ *
+ * @param child view to add.
+ * @param index index of the CellLayout children where to add the view.
+ * @param childId id of the view.
+ * @param params represent the logic of the view on the CellLayout.
+ * @param markCells if the occupied cells should be marked or not
+ * @return if adding the view was successful
+ */
+ public boolean addViewToCellLayout(View child, int index, int childId,
+ CellLayoutLayoutParams params, boolean markCells) {
+ final CellLayoutLayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
@@ -1046,7 +1057,7 @@
ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
if (clc.indexOfChild(child) != -1 && (child instanceof Reorderable)) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
final ItemInfo info = (ItemInfo) child.getTag();
final Reorderable item = (Reorderable) child;
@@ -1153,7 +1164,7 @@
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
- LayoutParams cell = mDragOutlines[mDragOutlineCurrent];
+ CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent];
cell.cellX = cellX;
cell.cellY = cellY;
cell.cellHSpan = spanX;
@@ -1708,7 +1719,7 @@
// cluster.
if (!cluster.views.contains(v) && v != dragView) {
if (cluster.isViewTouchingEdge(v, whichEdge)) {
- LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
if (!lp.canReorder) {
// The push solution includes the all apps button, this is not viable.
fail = true;
@@ -1919,7 +1930,7 @@
for (View child: solution.map.keySet()) {
if (child == ignoreView) continue;
CellAndSpan c = solution.map.get(child);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
if (Rect.intersects(r0, r1)) {
if (!lp.canReorder) {
@@ -2016,7 +2027,7 @@
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
CellAndSpan c;
if (temp) {
c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
@@ -2034,7 +2045,7 @@
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
CellAndSpan c = solution.map.get(child);
if (c != null) {
lp.tmpCellX = c.cellX;
@@ -2082,7 +2093,7 @@
!= null && !solution.intersectingViews.contains(child);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (c != null && !skip && (child instanceof Reorderable)) {
ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child,
mode, lp.cellX, lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY);
@@ -2273,7 +2284,7 @@
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
ItemInfo info = (ItemInfo) child.getTag();
// We do a null check here because the item info can be null in the case of the
// AllApps button in the hotseat.
@@ -2298,7 +2309,8 @@
private void setUseTempCoords(boolean useTempCoords) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
- LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mShortcutsAndWidgets.getChildAt(
+ i).getLayoutParams();
lp.useTmpCoords = useTempCoords;
}
}
@@ -2383,7 +2395,8 @@
for (int i = 0; i < count; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
if (Rect.intersects(r0, r1)) {
mIntersectingViews.add(child);
@@ -2408,7 +2421,8 @@
final int count = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < count; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
lp.tmpCellX = lp.cellX;
lp.tmpCellY = lp.cellY;
@@ -2685,7 +2699,8 @@
*/
void onDropChild(View child) {
if (child != null) {
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
lp.dropped = true;
child.requestLayout();
markCellsAsOccupiedForView(child);
@@ -2727,7 +2742,8 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
}
@@ -2739,7 +2755,8 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
}
@@ -2763,165 +2780,17 @@
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new CellLayout.LayoutParams(getContext(), attrs);
+ return new CellLayoutLayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof CellLayout.LayoutParams;
+ return p instanceof CellLayoutLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new CellLayout.LayoutParams(p);
- }
-
- public static class LayoutParams extends ViewGroup.MarginLayoutParams {
- /**
- * Horizontal location of the item in the grid.
- */
- @ViewDebug.ExportedProperty
- public int cellX;
-
- /**
- * Vertical location of the item in the grid.
- */
- @ViewDebug.ExportedProperty
- public int cellY;
-
- /**
- * Temporary horizontal location of the item in the grid during reorder
- */
- public int tmpCellX;
-
- /**
- * Temporary vertical location of the item in the grid during reorder
- */
- public int tmpCellY;
-
- /**
- * Indicates that the temporary coordinates should be used to layout the items
- */
- public boolean useTmpCoords;
-
- /**
- * Number of cells spanned horizontally by the item.
- */
- @ViewDebug.ExportedProperty
- public int cellHSpan;
-
- /**
- * Number of cells spanned vertically by the item.
- */
- @ViewDebug.ExportedProperty
- public int cellVSpan;
-
- /**
- * Indicates whether the item will set its x, y, width and height parameters freely,
- * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
- */
- public boolean isLockedToGrid = true;
-
- /**
- * Indicates whether this item can be reordered. Always true except in the case of the
- * the AllApps button and QSB place holder.
- */
- public boolean canReorder = true;
-
- // X coordinate of the view in the layout.
- @ViewDebug.ExportedProperty
- public int x;
- // Y coordinate of the view in the layout.
- @ViewDebug.ExportedProperty
- public int y;
-
- boolean dropped;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(LayoutParams source) {
- super(source);
- this.cellX = source.cellX;
- this.cellY = source.cellY;
- this.cellHSpan = source.cellHSpan;
- this.cellVSpan = source.cellVSpan;
- }
-
- public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
- super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- this.cellX = cellX;
- this.cellY = cellY;
- this.cellHSpan = cellHSpan;
- this.cellVSpan = cellVSpan;
- }
-
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
- int rowCount, Point borderSpace, @Nullable Rect inset) {
- setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
- borderSpace, inset);
- }
-
- /**
- * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
- * if the view needs to be scaled.
- *
- * ie. In multi-window mode, we setup widgets so that they are measured and laid out
- * using their full/invariant device profile sizes.
- */
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
- int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
- @Nullable Rect inset) {
- if (isLockedToGrid) {
- final int myCellHSpan = cellHSpan;
- final int myCellVSpan = cellVSpan;
- int myCellX = useTmpCoords ? tmpCellX : cellX;
- int myCellY = useTmpCoords ? tmpCellY : cellY;
-
- if (invertHorizontally) {
- myCellX = colCount - myCellX - cellHSpan;
- }
-
- int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
- int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
-
- float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
- float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
-
- width = Math.round(myCellWidth) - leftMargin - rightMargin;
- height = Math.round(myCellHeight) - topMargin - bottomMargin;
- x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
- y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
-
- if (inset != null) {
- x -= inset.left;
- y -= inset.top;
- width += inset.left + inset.right;
- height += inset.top + inset.bottom;
- }
- }
- }
-
- /**
- * Sets the position to the provided point
- */
- public void setCellXY(Point point) {
- cellX = point.x;
- cellY = point.y;
- }
-
- public String toString() {
- return "(" + this.cellX + ", " + this.cellY + ")";
- }
+ return new CellLayoutLayoutParams(p);
}
// This class stores info for two purposes:
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 377843c..eb68adb 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -772,9 +772,9 @@
}
if (mScroller.isFinished() && pageScrollChanged) {
- // TODO(b/238461210): Remove logging once root cause of flake detected.
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
if (Utilities.IS_RUNNING_IN_TEST_HARNESS && !(this instanceof Workspace)) {
- Log.d("b/238461210", this.getClass().getSimpleName() + "#onLayout() -> "
+ Log.d("b/246283207", this.getClass().getSimpleName() + "#onLayout() -> "
+ "if(mScroller.isFinished() && pageScrollChanged) -> getNextPage(): "
+ getNextPage() + ", getScrollForPage(getNextPage()): "
+ getScrollForPage(getNextPage()));
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 486a68f..8b342ea 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -32,6 +32,7 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
@@ -80,7 +81,7 @@
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
&& (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
@@ -107,7 +108,7 @@
}
public void setupLp(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (child instanceof NavigableAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
@@ -131,7 +132,7 @@
}
public void measureChild(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
final DeviceProfile dp = mActivity.getDeviceProfile();
if (child instanceof NavigableAppWidgetHostView) {
@@ -175,7 +176,6 @@
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
layoutChild(child);
}
}
@@ -185,7 +185,7 @@
* Core logic to layout a child for this ViewGroup.
*/
public void layoutChild(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (child instanceof NavigableAppWidgetHostView) {
NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child;
@@ -255,7 +255,7 @@
@Override
public void drawFolderLeaveBehindForIcon(FolderIcon child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
// While the folder is open, the position of the icon cannot change.
lp.canReorder = false;
if (mContainerType == HOTSEAT) {
@@ -266,7 +266,7 @@
@Override
public void clearFolderLeaveBehind(FolderIcon child) {
- ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) child.getLayoutParams()).canReorder = true;
if (mContainerType == HOTSEAT) {
CellLayout cl = (CellLayout) getParent();
cl.clearFolderLeaveBehind();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b49d646..6aff83e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,6 +67,7 @@
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
@@ -106,6 +107,7 @@
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -152,6 +154,8 @@
public static final int DEFAULT_PAGE = 0;
+ private final int mAllAppsIconSize;
+
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -286,7 +290,7 @@
mLauncher = Launcher.getLauncher(context);
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
mWallpaperManager = WallpaperManager.getInstance(context);
-
+ mAllAppsIconSize = mLauncher.getDeviceProfile().allAppsIconSizePx;
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
setHapticFeedbackEnabled(false);
@@ -559,7 +563,7 @@
}
int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, cellHSpan, 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
lp.canReorder = false;
if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
@@ -1671,8 +1675,14 @@
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
- if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
- dragOptions.preDragCondition = ((BubbleTextView) child).startLongPressAction();
+ if (child instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) child;
+ if (!dragOptions.isAccessibleDrag) {
+ dragOptions.preDragCondition = btv.startLongPressAction();
+ }
+ if (btv.isDisplaySearchResult()) {
+ dragOptions.preDragEndScale = (float) mAllAppsIconSize / btv.getIconSize();
+ }
}
final DragView dv;
@@ -1796,7 +1806,7 @@
boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
@@ -1831,7 +1841,7 @@
}
boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
@@ -2048,7 +2058,7 @@
}
// update the item's position after drop
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
lp.cellX = lp.tmpCellX = mTargetCell[0];
lp.cellY = lp.tmpCellY = mTargetCell[1];
lp.cellHSpan = item.spanX;
@@ -2074,7 +2084,7 @@
}
// If we can't find a drop location, we return the item to its original position
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
mTargetCell[0] = lp.cellX;
mTargetCell[1] = lp.cellY;
CellLayout layout = (CellLayout) cell.getParent().getParent();
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 7e6e1b6..0b3a62f 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -19,6 +19,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
@@ -111,11 +112,11 @@
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
- CellLayout.LayoutParams lp;
- if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
- lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
+ CellLayoutLayoutParams lp;
+ if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
+ lp = new CellLayoutLayoutParams(x, y, spanX, spanY);
} else {
- lp = (CellLayout.LayoutParams) genericLp;
+ lp = (CellLayoutLayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 79214e8..dd47592 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -23,6 +23,7 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
import com.android.launcher3.dragndrop.DragView;
@@ -244,7 +245,7 @@
}
private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) host.getLayoutParams();
CellLayout layout = (CellLayout) host.getParent().getParent();
layout.markCellsAsUnoccupiedForView(host);
diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
new file mode 100644
index 0000000..b14ae8d
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Represents the logic of where a view is in a CellLayout and its size
+ */
+public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams {
+ /**
+ * Horizontal location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellX;
+
+ /**
+ * Vertical location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellY;
+
+ /**
+ * Temporary horizontal location of the item in the grid during reorder
+ */
+ public int tmpCellX;
+
+ /**
+ * Temporary vertical location of the item in the grid during reorder
+ */
+ public int tmpCellY;
+
+ /**
+ * Indicates that the temporary coordinates should be used to layout the items
+ */
+ public boolean useTmpCoords;
+
+ /**
+ * Number of cells spanned horizontally by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellHSpan;
+
+ /**
+ * Number of cells spanned vertically by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellVSpan;
+
+ /**
+ * Indicates whether the item will set its x, y, width and height parameters freely,
+ * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
+ */
+ public boolean isLockedToGrid = true;
+
+ /**
+ * Indicates whether this item can be reordered. Always true except in the case of the
+ * the AllApps button and QSB place holder.
+ */
+ public boolean canReorder = true;
+
+ // X coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ public int x;
+ // Y coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ public int y;
+
+ public boolean dropped;
+
+ public CellLayoutLayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public CellLayoutLayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public CellLayoutLayoutParams(CellLayoutLayoutParams source) {
+ super(source);
+ this.cellX = source.cellX;
+ this.cellY = source.cellY;
+ this.cellHSpan = source.cellHSpan;
+ this.cellVSpan = source.cellVSpan;
+ }
+
+ public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
+ super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT);
+ this.cellX = cellX;
+ this.cellY = cellY;
+ this.cellHSpan = cellHSpan;
+ this.cellVSpan = cellVSpan;
+ }
+
+ /**
+ * Updates the {@link CellLayoutLayoutParams} with the right measures using their
+ * full/invariant device profile sizes.
+ */
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, Point borderSpace, @Nullable Rect inset) {
+ setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
+ borderSpace, inset);
+ }
+
+ /**
+ * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
+ * if the view needs to be scaled.
+ *
+ * ie. In multi-window mode, we setup widgets so that they are measured and laid out
+ * using their full/invariant device profile sizes.
+ */
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
+ @Nullable Rect inset) {
+ if (isLockedToGrid) {
+ final int myCellHSpan = cellHSpan;
+ final int myCellVSpan = cellVSpan;
+ int myCellX = useTmpCoords ? tmpCellX : cellX;
+ int myCellY = useTmpCoords ? tmpCellY : cellY;
+
+ if (invertHorizontally) {
+ myCellX = colCount - myCellX - cellHSpan;
+ }
+
+ int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
+ int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
+
+ float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
+ float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
+
+ width = Math.round(myCellWidth) - leftMargin - rightMargin;
+ height = Math.round(myCellHeight) - topMargin - bottomMargin;
+ x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
+ y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
+
+ if (inset != null) {
+ x -= inset.left;
+ y -= inset.top;
+ width += inset.left + inset.right;
+ height += inset.top + inset.bottom;
+ }
+ }
+ }
+
+ /**
+ * Sets the position to the provided point
+ */
+ public void setCellXY(Point point) {
+ cellX = point.x;
+ cellY = point.y;
+ }
+
+ /**
+ * @return the string representation of the position of the {@link CellLayoutLayoutParams}
+ */
+ public String toString() {
+ return "(" + this.cellX + ", " + this.cellY + ")";
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 8616f35..5368397 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -31,6 +31,7 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -90,6 +91,8 @@
protected boolean mIsInPreDrag;
+ private final int DRAG_VIEW_SCALE_DURATION_MS = 500;
+
/**
* Interface to receive notifications when a drag starts or stops
*/
@@ -214,6 +217,15 @@
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
}
mIsInPreDrag = false;
+ if (mOptions.preDragEndScale != 0) {
+ mDragObject.dragView
+ .animate()
+ .scaleX(mOptions.preDragEndScale)
+ .scaleY(mOptions.preDragEndScale)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .setDuration(DRAG_VIEW_SCALE_DURATION_MS)
+ .start();
+ }
mDragObject.dragView.onDragStart();
for (DragListener listener : new ArrayList<>(mListeners)) {
listener.onDragStart(mDragObject, mOptions);
@@ -295,9 +307,9 @@
} else if (mIsInPreDrag) {
animateDragViewToOriginalPosition(null, null, -1);
}
+ mDragObject.dragView.clearAnimation();
mDragObject.dragView = null;
}
-
// Only end the drag if we are not deferred
if (!isDeferred) {
callOnDragEnd();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 8eeca7d..4bea0ad 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -43,7 +43,6 @@
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -51,6 +50,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringProperty;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.Scrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -237,7 +237,7 @@
View anchorView) {
ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
parentChildren.measureChild(child);
parentChildren.layoutChild(child);
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index e8ff8da..1ff4335 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -40,6 +40,12 @@
/** Determines when a pre-drag should transition to a drag. By default, this is immediate. */
public PreDragCondition preDragCondition = null;
+ /**
+ * A drag scale that scales the original drag view size when the preDragCondition is met (or
+ * is ignored if preDragEndScale is 0).
+ */
+ public float preDragEndScale;
+
/** Scale of the icons over the workspace icon size. */
public float intrinsicIconScaleFactor = 1f;
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 61ffd9d..d5ef9df 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -43,6 +43,7 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -215,6 +216,7 @@
final int footerStartDelay;
if (isLargeFolder()) {
if (mIsOpening) {
+ mFolder.mFooter.setAlpha(0);
footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION;
footerStartDelay = mDuration - footerAlphaDuration;
} else {
@@ -341,7 +343,7 @@
ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
for (int i = 0; i < numItemsInPreview; ++i) {
final BubbleTextView btv = itemsInPreview.get(i);
- CellLayout.LayoutParams btvLp = (CellLayout.LayoutParams) btv.getLayoutParams();
+ CellLayoutLayoutParams btvLp = (CellLayoutLayoutParams) btv.getLayoutParams();
// Calculate the final values in the LayoutParams.
btvLp.isLockedToGrid = true;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 33e0902..e8759da 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -57,6 +57,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -277,7 +278,7 @@
public void onDragEnter(ItemInfo dragInfo) {
if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams();
CellLayout cl = (CellLayout) getParent().getParent();
mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3d5aef5..0bcb97a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -41,6 +41,7 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -202,7 +203,7 @@
public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
int pageNo = rank / mOrganizer.getMaxItemsPerPage();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
lp.setCellXY(mOrganizer.getPosForRank(rank));
getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
}
@@ -218,9 +219,9 @@
textView.setOnClickListener(ItemClickHandler.INSTANCE);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) textView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams();
if (lp == null) {
- textView.setLayoutParams(new CellLayout.LayoutParams(
+ textView.setLayoutParams(new CellLayoutLayoutParams(
item.cellX, item.cellY, item.spanX, item.spanY));
} else {
lp.cellX = item.cellX;
@@ -314,7 +315,7 @@
}
if (v != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
ItemInfo info = (ItemInfo) v.getTag();
lp.setCellXY(mOrganizer.getPosForRank(rank));
currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index c1bab54..482e923 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -72,6 +72,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceLayoutManager;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
@@ -532,8 +533,8 @@
CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen,
false);
- CellLayout.LayoutParams lp =
- new CellLayout.LayoutParams(0, 0, firstScreen.getCountX(), 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, firstScreen.getCountX(),
+ 1);
lp.canReorder = false;
firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 466f63f..a8e9eb5 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -350,9 +350,11 @@
break;
case ITEM_TYPE_TASK:
itemBuilder
- .setTask(LauncherAtom.Task.newBuilder()
- .setComponentName(getTargetComponent().flattenToShortString())
- .setIndex(screenId));
+ .setTask(nullableComponent
+ .map(component -> LauncherAtom.Task.newBuilder()
+ .setComponentName(component.flattenToShortString())
+ .setIndex(screenId))
+ .orElse(LauncherAtom.Task.newBuilder()));
break;
default:
break;
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 55afc28..5efebaa 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -618,15 +618,17 @@
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
if (isLandscape) {
- outRect.right = outRect.left + (int) (outRect.width() * topLeftTaskPercent);
+ outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
} else {
- outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
+ outRect.bottom = outRect.top + Math.round(outRect.height() * topLeftTaskPercent);
}
} else {
if (isLandscape) {
- outRect.left += (int) (outRect.width() * (topLeftTaskPercent + dividerBarPercent));
+ outRect.left += Math.round(outRect.width()
+ * (topLeftTaskPercent + dividerBarPercent));
} else {
- outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
+ outRect.top += Math.round(outRect.height()
+ * (topLeftTaskPercent + dividerBarPercent));
}
}
}
@@ -639,7 +641,7 @@
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
int dividerBar = Math.round(splitBoundsConfig.appsStackedVertically
? splitBoundsConfig.dividerHeightPercent * totalThumbnailHeight
- : splitBoundsConfig.dividerWidthPercent * totalThumbnailHeight);
+ : splitBoundsConfig.dividerWidthPercent * parentWidth);
int primarySnapshotHeight;
int primarySnapshotWidth;
int secondarySnapshotHeight;
@@ -648,7 +650,7 @@
splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
if (dp.isLandscape) {
primarySnapshotHeight = totalThumbnailHeight;
- primarySnapshotWidth = (int) (parentWidth * taskPercent);
+ primarySnapshotWidth = Math.round(parentWidth * taskPercent);
secondarySnapshotHeight = totalThumbnailHeight;
secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar;
@@ -663,7 +665,7 @@
secondarySnapshot.setTranslationY(spaceAboveSnapshot);
} else {
primarySnapshotWidth = parentWidth;
- primarySnapshotHeight = (int) (totalThumbnailHeight * taskPercent);
+ primarySnapshotHeight = Math.round(totalThumbnailHeight * taskPercent);
secondarySnapshotWidth = parentWidth;
secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 800b1f6..1e154a2 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -273,6 +273,12 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getActionIndex() > 0) {
+ // This means there is multiple touch inputs, ignore it, we could also cancel the
+ // previous touch but the user might cancel the drag by accident.
+ return true;
+ }
+
switch (ev.getAction()) {
case ACTION_DOWN: {
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 07bfe4c..cf5f5fc 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -18,8 +18,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import android.platform.test.annotations.IwTest;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -31,6 +29,7 @@
import android.content.Intent;
import android.graphics.Point;
+import android.platform.test.annotations.IwTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -437,6 +436,23 @@
@Test
@PortraitLandscape
+ public void testDragAndCancelAppIcon() {
+ final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
+ Point positionBeforeDrag =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+ assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag);
+
+ mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon);
+
+ Point positionAfterDrag =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+ assertNotNull("App not found in Workspace after dragging.", positionAfterDrag);
+ assertEquals("App not returned to same position in Workspace after drag & cancel",
+ positionBeforeDrag, positionAfterDrag);
+ }
+
+ @Test
+ @PortraitLandscape
public void testDeleteFromWorkspace() throws Exception {
// test delete both built-in apps and user-installed app from workspace
for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3545c27..56ba9ea 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -138,6 +138,15 @@
OUTSIDE_WITH_KEYCODE,
}
+ /**
+ * Represents a point in the code at which a callback can run.
+ */
+ public enum CALLBACK_RUN_POINT {
+ CALLBACK_HOLD_BEFORE_DROP
+ }
+
+ private Consumer<CALLBACK_RUN_POINT> mCallbackAtRunPoint = null;
+
// Base class for launcher containers.
abstract static class VisibleContainer {
protected final LauncherInstrumentation mLauncher;
@@ -1951,4 +1960,20 @@
LauncherInstrumentation.GestureScope.INSIDE);
}
}
+
+ /**
+ * Sets the consumer to run callbacks at all run-points.
+ */
+ public void setRunPointCallback(Consumer<CALLBACK_RUN_POINT> callback) {
+ mCallbackAtRunPoint = callback;
+ }
+
+ /**
+ * Runs the callback at the specified point if it exists.
+ */
+ void runCallbackIfActive(CALLBACK_RUN_POINT runPoint) {
+ if (mCallbackAtRunPoint != null) {
+ mCallbackAtRunPoint.accept(runPoint);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 2c9fdb3..425a90a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -18,6 +18,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
+import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOLD_BEFORE_DROP;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -302,6 +303,31 @@
}
/**
+ * Drag the appIcon from the workspace and cancel by dragging icon to corner of screen where no
+ * drop point exists.
+ *
+ * @param homeAppIcon to be dragged.
+ */
+ @NonNull
+ public Workspace dragAndCancelAppIcon(HomeAppIcon homeAppIcon) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "dragging app icon across workspace")) {
+ dragIconToWorkspace(
+ mLauncher,
+ homeAppIcon,
+ () -> new Point(0, 0),
+ () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+ null);
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "dragged the app across workspace")) {
+ return new Workspace(mLauncher);
+ }
+ }
+ }
+
+ /**
* Delete the appIcon from the workspace.
*
* @param homeAppIcon to be deleted.
@@ -493,6 +519,7 @@
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.INSIDE);
+ launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP);
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
}
}