Snap for 8292183 from 635fa756eb to tm-release

Change-Id: I5f68a3eb1ef2d302ee0f818fa87913a31fb64dfa
This commit is contained in:
Android Build Coastguard Worker
2022-03-12 04:08:43 +00:00
76 changed files with 1855 additions and 267 deletions
+2 -3
View File
@@ -31,7 +31,6 @@ android_library {
"androidx.test.uiautomator_uiautomator",
"androidx.preference_preference",
"SystemUISharedLib",
"SystemUIAnimationLib",
],
srcs: [
"tests/tapl/**/*.java",
@@ -197,7 +196,6 @@ android_library {
"lottie",
"SystemUISharedLib",
"SystemUI-statsd",
"SystemUIAnimationLib",
],
manifest: "quickstep/AndroidManifest.xml",
min_sdk_version: "current",
@@ -210,6 +208,8 @@ filegroup {
srcs: [
"ext_tests/src/**/*.java",
"ext_tests/src/**/*.kt",
"quickstep/ext_tests/src/**/*.java",
"quickstep/ext_tests/src/**/*.kt",
],
}
@@ -289,7 +289,6 @@ android_library {
"SystemUISharedLib",
"Launcher3CommonDepsLib",
"QuickstepResLib",
"SystemUIAnimationLib",
],
manifest: "quickstep/AndroidManifest.xml",
platform_apis: true,
@@ -29,8 +29,10 @@ import android.view.View;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutAndWidgetContainer;
import java.util.ArrayList;
import java.util.Collection;
@@ -205,6 +207,32 @@ public class DebugTestInformationHandler extends TestInformationHandler {
}
}
case TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT: {
useTestWorkspaceLayout(true);
return response;
}
case TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT: {
useTestWorkspaceLayout(false);
return response;
}
case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
ShortcutAndWidgetContainer hotseatIconsContainer =
l.getHotseat().getShortcutsAndWidgets();
ArrayList<String> hotseatIconNames = new ArrayList<>();
for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
// Use unchecked cast to catch changes in hotseat layout
BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
hotseatIconNames.add((String) icon.getText());
}
return hotseatIconNames;
});
}
case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
return response;
@@ -223,4 +251,15 @@ public class DebugTestInformationHandler extends TestInformationHandler {
return super.call(method, arg, extras);
}
}
private void useTestWorkspaceLayout(boolean useTestWorkspaceLayout) {
final long identity = Binder.clearCallingIdentity();
try {
LauncherSettings.Settings.call(mContext.getContentResolver(), useTestWorkspaceLayout
? LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG
: LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@@ -0,0 +1,120 @@
/*
* 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;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.testing.DebugTestInformationHandler;
import com.android.launcher3.testing.TestProtocol;
import java.util.concurrent.ExecutionException;
/**
* Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
*/
public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
private final DebugTestInformationHandler mDebugTestInformationHandler;
public DebugQuickstepTestInformationHandler(Context context) {
super(context);
mDebugTestInformationHandler = new DebugTestInformationHandler(context);
}
@Override
public Bundle call(String method, String arg, @Nullable Bundle extras) {
Bundle response = new Bundle();
switch (method) {
case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
runOnUIThread(l -> {
enableManualTaskbarStashing(l, true);
});
return response;
case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
runOnUIThread(l -> {
enableManualTaskbarStashing(l, false);
});
return response;
case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
runOnUIThread(l -> {
enableManualTaskbarStashing(l, true);
BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) l;
LauncherTaskbarUIController taskbarUIController =
quickstepLauncher.getTaskbarUIController();
// Allow null-pointer to catch illegal states.
taskbarUIController.unstashTaskbarIfStashed();
enableManualTaskbarStashing(l, false);
});
return response;
case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
final Resources resources = mContext.getResources();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
return response;
}
default:
response = super.call(method, arg, extras);
if (response != null) return response;
return mDebugTestInformationHandler.call(method, arg, extras);
}
}
private void enableManualTaskbarStashing(Launcher launcher, boolean enable) {
BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) launcher;
LauncherTaskbarUIController taskbarUIController =
quickstepLauncher.getTaskbarUIController();
// Allow null-pointer to catch illegal states.
taskbarUIController.enableManualStashingForTests(enable);
}
/**
* Runs the given command on the UI thread.
*/
private static void runOnUIThread(UIThreadCommand command) {
try {
MAIN_EXECUTOR.submit(() -> {
command.execute(Launcher.ACTIVITY_TRACKER.getCreatedActivity());
return null;
}).get();
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
private interface UIThreadCommand {
void execute(Launcher launcher);
}
}
@@ -16,5 +16,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="?android:attr/dialogCornerRadius"/>
<solid android:color="@color/gesture_tutorial_primary_color"/>
<solid android:color="?android:attr/colorAccent"/>
</shape>
@@ -25,7 +25,7 @@
<shape
android:shape="rectangle">
<corners android:radius="50dp"/>
<solid android:color="@color/gesture_tutorial_primary_color"/>
<solid android:color="?android:attr/colorAccent"/>
</shape>
</item>
</layer-list>
@@ -17,5 +17,5 @@
android:shape="rectangle">
<corners android:radius="50dp"/>
<solid android:color="@android:color/transparent"/>
<stroke android:width="1dp" android:color="@color/gesture_tutorial_primary_color"/>
<stroke android:width="1dp" android:color="?android:attr/colorAccent"/>
</shape>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/gesture_tutorial_primary_color" />
<solid android:color="?android:attr/colorAccent" />
<size android:width="92dp" android:height="92dp"/>
</shape>
@@ -85,7 +85,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="0.25"
android:fillColor="@color/gesture_tutorial_primary_color"
android:fillColor="?android:attr/colorAccent"
android:fillType="nonZero"
android:pathData=" M12.5 -446 C12.5,-446 12.5,446 12.5,446 C12.5,446 -12.5,446 -12.5,446 C-12.5,446 -12.5,-446 -12.5,-446 C-12.5,-446 12.5,-446 12.5,-446c " />
</group>
@@ -81,7 +81,7 @@
<path
android:name="_R_G_L_1_G_D_0_P_0"
android:fillAlpha="0.25"
android:fillColor="@color/gesture_tutorial_primary_color"
android:fillColor="?android:attr/colorAccent"
android:fillType="nonZero"
android:pathData=" M206 -12.5 C206,-12.5 206,12.5 206,12.5 C206,12.5 -206,12.5 -206,12.5 C-206,12.5 -206,-12.5 -206,-12.5 C-206,-12.5 206,-12.5 206,-12.5c " />
</group>
@@ -81,7 +81,7 @@
<path
android:name="_R_G_L_1_G_D_0_P_0"
android:fillAlpha="0.25"
android:fillColor="@color/gesture_tutorial_primary_color"
android:fillColor="?android:attr/colorAccent"
android:fillType="nonZero"
android:pathData=" M206 -12.5 C206,-12.5 206,12.5 206,12.5 C206,12.5 -206,12.5 -206,12.5 C-206,12.5 -206,-12.5 -206,-12.5 C-206,-12.5 206,-12.5 206,-12.5c " />
</group>
+1 -3
View File
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<color name="chip_hint_foreground_color">#fff</color>
<color name="chip_scrim_start_color">#39000000</color>
@@ -41,8 +41,6 @@
<color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
<!-- Must contrast gesture_tutorial_fake_wallpaper_color -->
<color name="gesture_tutorial_fake_previous_task_view_color">#3C4043</color> <!-- Gray -->
<color name="gesture_tutorial_action_button_label_color">#FF000000</color>
<color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
<color name="gesture_tutorial_taskbar_color">#202124</color>
<!-- Mock hotseat -->
+5 -4
View File
@@ -88,7 +88,8 @@
<style name="TextAppearance.GestureTutorial.Feedback.Subtext"
parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
<item name="android:textColor">@color/gesture_tutorial_primary_color</item>
<item name="android:textSize">16sp</item>
<item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:gravity">start</item>
</style>
@@ -100,7 +101,7 @@
<style name="TextAppearance.GestureTutorial.ButtonLabel"
parent="TextAppearance.GestureTutorial.CallToAction">
<item name="android:gravity">center</item>
<item name="android:textColor">@color/gesture_tutorial_action_button_label_color</item>
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:letterSpacing">0.02</item>
<item name="android:textSize">16sp</item>
<item name="android:textAllCaps">false</item>
@@ -108,12 +109,12 @@
<style name="TextAppearance.GestureTutorial.CancelButtonLabel"
parent="TextAppearance.GestureTutorial.ButtonLabel">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextAppearance.GestureTutorial.TextButtonLabel"
parent="TextAppearance.GestureTutorial.ButtonLabel">
<item name="android:textColor">@color/gesture_tutorial_primary_color</item>
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextAppearance.GestureTutorial.LinkText"
@@ -44,6 +44,7 @@ import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVIT
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
@@ -77,6 +78,7 @@ import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.view.SurfaceControl;
@@ -109,6 +111,7 @@ import com.android.launcher3.util.Themes;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.ScrimView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.quickstep.LauncherBackAnimationController;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskViewUtils;
@@ -135,6 +138,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
@@ -213,6 +217,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
private RemoteTransitionCompat mLauncherOpenTransition;
private LauncherBackAnimationController mBackAnimationController;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -238,6 +243,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
mHandler = new Handler(Looper.getMainLooper());
mDeviceProfile = mLauncher.getDeviceProfile();
mBackAnimationController = new LauncherBackAnimationController(
mDeviceProfile, mLauncher, this);
Resources res = mLauncher.getResources();
mContentScale = res.getFloat(R.dimen.content_scale);
@@ -611,9 +618,28 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
RecentsView overview = mLauncher.getOverviewPanel();
ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
RecentsView.CONTENT_ALPHA, alphas);
Log.d(BAD_STATE, "QTM composeViewContentAnimator alphas=" + Arrays.toString(alphas));
alpha.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
Log.d(BAD_STATE, "QTM composeViewContentAnimator onStart");
}
@Override
public void onAnimationCancel(Animator animation) {
float alpha = overview == null ? -1 : RecentsView.CONTENT_ALPHA.get(overview);
Log.d(BAD_STATE, "QTM composeViewContentAnimator onCancel, alpha=" + alpha);
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d(BAD_STATE, "QTM composeViewContentAnimator onEnd");
}
});
alpha.setDuration(CONTENT_ALPHA_DURATION);
alpha.setInterpolator(LINEAR);
anim.play(alpha);
Log.d(BAD_STATE, "QTM composeViewContentAnimator setFreezeVisibility=true");
overview.setFreezeViewVisibility(true);
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(overview, SCALE_PROPERTY, scales);
@@ -622,6 +648,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
anim.play(scaleAnim);
return () -> {
Log.d(BAD_STATE, "QTM composeViewContentAnimator onEnd setFreezeVisibility=false");
overview.setFreezeViewVisibility(false);
SCALE_PROPERTY.set(overview, 1f);
mLauncher.getStateManager().reapplyState();
@@ -1136,6 +1163,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
}
if (mBackAnimationController != null) {
mBackAnimationController.registerBackCallbacks(mHandler);
}
}
public void onActivityDestroyed() {
@@ -1171,6 +1201,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mLauncherOpenTransition = null;
mWallpaperOpenTransitionRunner = null;
}
if (mBackAnimationController != null) {
mBackAnimationController.unregisterBackCallbacks();
mBackAnimationController = null;
}
}
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
@@ -1323,8 +1357,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
/**
* Closing animator that animates the window into its final location on the workspace.
*/
private void getClosingWindowAnimators(AnimatorSet animation,
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) {
private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
RectF closingWindowStartRect) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
@@ -1356,8 +1391,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
targetRect.set(getDefaultWindowTargetRect());
}
final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher,
RectFSpringAnim anim = new RectFSpringAnim(closingWindowStartRect, targetRect, mLauncher,
mDeviceProfile);
// Hook up floating views to the closing window animators.
@@ -1391,7 +1425,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
FloatingWidgetView finalFloatingWidget = floatingWidget;
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
windowTargetBounds) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
@@ -1415,6 +1449,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
anim.start(mLauncher, velocityPxPerS);
}
});
return anim;
}
/**
@@ -1538,6 +1573,97 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
});
}
/**
* Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate
* the transition.
*/
public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
boolean fromUnlock,
RectF startRect) {
AnimatorSet anim = null;
RectFSpringAnim rectFSpringAnim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
}
if (anim == null) {
anim = new AnimatorSet();
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
View launcherView = findLauncherView(appTargets);
boolean playFallBackAnimation = (launcherView == null
&& launcherIsForceInvisibleOrOpening)
|| mLauncher.getWorkspace().isOverlayShown()
|| hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
boolean playWorkspaceReveal = true;
boolean skipAllAppsScale = false;
if (fromUnlock) {
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
&& !playFallBackAnimation) {
// Use a fixed velocity to start the animation.
float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
PointF velocity = new PointF(0, -velocityPxPerS);
rectFSpringAnim = getClosingWindowAnimators(
anim, appTargets, launcherView, velocity, startRect);
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
true /* animateOverviewScrim */, launcherView).getAnimators());
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
playWorkspaceReveal = false;
} else {
// Skip scaling all apps, otherwise FloatingIconView will get wrong
// layout bounds.
skipAllAppsScale = true;
}
} else {
anim.play(getFallbackClosingWindowAnimators(appTargets));
}
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
// contents of launcher unless we know that we are animating home as a result
// of the home button press with quickstep, which will result in launcher being
// started on touch down, prior to the animation home (and won't be in the
// targets list because it is already visible). In that case, we force
// invisibility on touch down, and only reset it after the animation to home
// is initialized.
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
skipAllAppsScale);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
contentAnimator.second.run();
}
});
} else {
if (playWorkspaceReveal) {
anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
}
}
}
}
return new Pair(rectFSpringAnim, anim);
}
/**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
@@ -1578,84 +1704,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mLauncher.getStateManager().moveToRestState();
}
AnimatorSet anim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
}
if (anim == null) {
anim = new AnimatorSet();
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
View launcherView = findLauncherView(appTargets);
boolean playFallBackAnimation = (launcherView == null
&& launcherIsForceInvisibleOrOpening)
|| mLauncher.getWorkspace().isOverlayShown()
|| hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
boolean playWorkspaceReveal = true;
boolean skipAllAppsScale = false;
if (mFromUnlock) {
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
&& !playFallBackAnimation) {
// Use a fixed velocity to start the animation.
float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
PointF velocity = new PointF(0, -velocityPxPerS);
getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
true /* animateOverviewScrim */, launcherView).getAnimators());
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
playWorkspaceReveal = false;
} else {
// Skip scaling all apps, otherwise FloatingIconView will get wrong
// layout bounds.
skipAllAppsScale = true;
}
} else {
anim.play(getFallbackClosingWindowAnimators(appTargets));
}
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
// contents of launcher unless we know that we are animating home as a result
// of the home button press with quickstep, which will result in launcher being
// started on touch down, prior to the animation home (and won't be in the
// targets list because it is already visible). In that case, we force
// invisibility on touch down, and only reset it after the animation to home
// is initialized.
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
skipAllAppsScale);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
contentAnimator.second.run();
}
});
} else {
if (playWorkspaceReveal) {
anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
}
}
}
}
Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
appTargets, wallpaperTargets, mFromUnlock,
new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx));
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
result.setAnimation(anim, mLauncher);
result.setAnimation(pair.second, mLauncher);
}
}
@@ -194,7 +194,7 @@ public class AppsDividerView extends View implements FloatingHeaderRow {
@Override
protected void onDraw(Canvas canvas) {
if (mDividerType == DividerType.LINE) {
int l = (getWidth() - getPaddingLeft() - mDividerSize[0]) / 2;
int l = (getWidth() - mDividerSize[0]) / 2;
int t = getHeight() - (getPaddingBottom() / 2);
int radius = mDividerSize[1];
canvas.drawRoundRect(l, t, l + mDividerSize[0], t + mDividerSize[1], radius, radius,
@@ -27,6 +27,7 @@ import android.view.TaskTransitionSpec;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
@@ -117,6 +118,24 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
shouldDelayLauncherStateAnim);
}
/**
* Enables manual taskbar stashing. This method should only be used for tests that need to
* stash/unstash the taskbar.
*/
@VisibleForTesting
public void enableManualStashingForTests(boolean enableManualStashing) {
mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
}
/**
* Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
* taskbar at the end of a test.
*/
@VisibleForTesting
public void unstashTaskbarIfStashed() {
mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
}
/**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
@@ -71,6 +71,8 @@ import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.NavigationMode;
@@ -646,12 +648,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
intent = new PackageManagerHelper(this)
.getMarketIntent(info.getTargetPackage())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
String id = info.getDeepShortcutId();
String packageName = intent.getPackage();
getSystemService(LauncherApps.class)
@@ -680,6 +686,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
if (info.user.equals(Process.myUserHandle())) {
// TODO(b/216683257): Use startActivityForResult for search results that require it.
startActivity(intent);
@@ -116,7 +116,7 @@ public class TaskbarControllers {
taskbarEduController.init(this);
taskbarPopupController.init(this);
taskbarForceVisibleImmersiveController.init(this);
taskbarAllAppsController.init(this);
taskbarAllAppsController.init(this, sharedState);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -152,6 +152,7 @@ public class TaskbarControllers {
taskbarAutohideSuspendController.onDestroy();
taskbarPopupController.onDestroy();
taskbarForceVisibleImmersiveController.onDestroy();
taskbarAllAppsController.onDestroy();
mControllersToLog = null;
}
@@ -63,6 +63,8 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.systemui.shared.recents.model.Task;
@@ -128,7 +130,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
if (!(view instanceof BubbleTextView)) {
return false;
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
BubbleTextView btv = (BubbleTextView) view;
mActivity.onDragStart();
btv.post(() -> {
@@ -164,24 +166,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
dragLayerY += dragRect.top;
DragOptions dragOptions = new DragOptions();
dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
private DragView mDragView;
@Override
public boolean shouldStartDrag(double distanceDragged) {
return mDragView != null && mDragView.isAnimationFinished();
}
@Override
public void onPreDragStart(DropTarget.DragObject dragObject) {
mDragView = dragObject.dragView;
}
@Override
public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
mDragView = null;
}
};
dragOptions.preDragCondition = null;
if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
mControllers.taskbarPopupController.showForIcon(btv);
@@ -189,6 +174,32 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
}
}
if (dragOptions.preDragCondition == null) {
dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
private DragView mDragView;
@Override
public boolean shouldStartDrag(double distanceDragged) {
return mDragView != null && mDragView.isAnimationFinished();
}
@Override
public void onPreDragStart(DropTarget.DragObject dragObject) {
mDragView = dragObject.dragView;
if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()
&& !shouldStartDrag(0)) {
// Immediately close the popup menu.
mDragView.setOnAnimationEndCallback(() -> callOnDragStart());
}
}
@Override
public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
mDragView = null;
}
};
}
return startDrag(
drawable,
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -184,6 +185,9 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
} else if (mControllers.taskbarDragController.isSystemDragInProgress()) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
} else if (AbstractFloatingView.getOpenView(mActivity, TYPE_TASKBAR_ALL_APPS) != null) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
} else if (mControllers.taskbarViewController.areIconsVisible()
|| AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null
|| mActivity.isNavBarKidsModeActive()) {
@@ -29,16 +29,13 @@ import androidx.annotation.NonNull;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.animation.ViewRootSync;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.HashMap;
@@ -79,9 +76,6 @@ import java.util.function.Supplier;
private boolean mShouldDelayLauncherStateAnim;
// We skip any view synchronizations during init/destroy.
private boolean mCanSyncViews;
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
@@ -108,8 +102,6 @@ import java.util.function.Supplier;
};
public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
mCanSyncViews = false;
mControllers = controllers;
mLauncher = launcher;
@@ -129,13 +121,9 @@ import java.util.function.Supplier;
updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed());
mLauncherState = launcher.getStateManager().getState();
applyState(0);
mCanSyncViews = true;
}
public void onDestroy() {
mCanSyncViews = false;
mIconAlignmentForResumedState.finishAnimation();
mIconAlignmentForGestureState.finishAnimation();
mIconAlignmentForLauncherState.finishAnimation();
@@ -143,8 +131,6 @@ import java.util.function.Supplier;
mIconAlphaForHome.setConsumer(null);
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
mCanSyncViews = true;
}
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@@ -394,27 +380,6 @@ import java.util.function.Supplier;
return;
}
float alignment = alignmentSupplier.get();
float currentValue = mIconAlphaForHome.getValue();
boolean taskbarWillBeVisible = alignment < 1;
boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
|| (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
// Sync the first frame where we swap taskbar and hotseat.
if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
DeviceProfile dp = mLauncher.getDeviceProfile();
// Do all the heavy work before the sync.
mControllers.taskbarViewController.createIconAlignmentControllerIfNotExists(dp);
ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(),
mControllers.taskbarActivityContext.getDragLayer(),
() -> updateIconAlignment(alignment));
} else {
updateIconAlignment(alignment);
}
}
private void updateIconAlignment(float alignment) {
mControllers.taskbarViewController.setLauncherIconAlignment(
alignment, mLauncher.getDeviceProfile());
@@ -145,6 +145,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
});
// TODO (b/198438631): configure for taskbar/context
container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
mControllers.taskbarDragController.addDragListener(container);
container.populateAndShow(icon,
mPopupDataProvider.getShortcutCountForItem(item),
@@ -24,4 +24,6 @@ public class TaskbarSharedState {
public boolean setupUIVisible = false;
public boolean allAppsVisible = false;
}
@@ -136,6 +136,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private boolean mIsSystemGestureInProgress;
private boolean mIsImeShowing;
private boolean mEnableManualStashingForTests = false;
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
flags -> {
@@ -199,12 +201,15 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
&& (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
&& (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingForTests);
}
private boolean supportsStashingForTests() {
// TODO: enable this for tests that specifically check stash/unstash behavior.
return false;
/**
* Enables support for manual stashing. This should only be used to add this functionality
* to Launcher specific tests.
*/
public void enableManualStashingForTests(boolean enableManualStashing) {
mEnableManualStashingForTests = enableManualStashing;
}
/**
@@ -182,16 +182,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
+ mTaskbarIconTranslationYForStash.value);
}
/**
* Creates the icon alignment controller if it does not already exist.
* @param launcherDp Launcher device profile.
*/
public void createIconAlignmentControllerIfNotExists(DeviceProfile launcherDp) {
if (mIconAlignControllerLazy == null) {
mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
}
/**
* Sets the taskbar icon alignment relative to Launcher hotseat icons
* @param alignmentRatio [0, 1]
@@ -199,7 +189,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
* 1 => fully aligned
*/
public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
createIconAlignmentControllerIfNotExists(launcherDp);
if (mIconAlignControllerLazy == null) {
mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
if (alignmentRatio <= 0 || alignmentRatio >= 1) {
// Cleanup lazy controller so that it is created again in next animation
@@ -24,6 +24,7 @@ import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsI
import android.content.Context;
import android.graphics.Insets;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
@@ -38,6 +39,8 @@ import com.android.launcher3.taskbar.BaseTaskbarContext;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -166,7 +169,6 @@ class TaskbarAllAppsContext extends BaseTaskbarContext {
super.onAttachedToWindow();
ViewTreeObserverWrapper.addOnComputeInsetsListener(
getViewTreeObserver(), this);
mActivity.mAllAppsViewController.show();
}
@Override
@@ -180,6 +182,12 @@ class TaskbarAllAppsContext extends BaseTaskbarContext {
mControllers = new TouchController[]{mActivity.mDragController};
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
@@ -38,6 +38,9 @@ import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.List;
import java.util.Optional;
@@ -61,7 +64,15 @@ public final class TaskbarAllAppsController implements OnDeviceProfileChangeList
private final TaskbarAllAppsProxyView mProxyView;
private final LayoutParams mLayoutParams;
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
mProxyView.close(false);
}
};
private TaskbarControllers mControllers;
private TaskbarSharedState mSharedState;
/** Window context for all apps if it is open. */
private @Nullable TaskbarAllAppsContext mAllAppsContext;
@@ -77,9 +88,19 @@ public final class TaskbarAllAppsController implements OnDeviceProfileChangeList
}
/** Initialize the controller. */
public void init(TaskbarControllers controllers) {
if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
mControllers = controllers;
public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
return;
}
mControllers = controllers;
mSharedState = sharedState;
/*
* Recreate All Apps if it was open in the previous Taskbar instance (e.g. the configuration
* changed).
*/
if (mSharedState.allAppsVisible) {
show(false);
}
}
@@ -112,16 +133,22 @@ public final class TaskbarAllAppsController implements OnDeviceProfileChangeList
/** Opens the {@link TaskbarAllAppsContainerView} in a new window. */
public void show() {
show(true);
}
private void show(boolean animate) {
if (mProxyView.isOpen()) {
return;
}
mProxyView.show();
mSharedState.allAppsVisible = true;
mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext,
this,
mControllers.taskbarStashController);
mAllAppsContext.getDragController().init(mControllers);
mTaskbarContext.addOnDeviceProfileChangeListener(this);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
.ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
@@ -129,6 +156,7 @@ public final class TaskbarAllAppsController implements OnDeviceProfileChangeList
mAllAppsContext.getAppsView().getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
mAllAppsContext.getAllAppsViewController().show(animate);
}
/** Closes the {@link TaskbarAllAppsContainerView}. */
@@ -148,6 +176,13 @@ public final class TaskbarAllAppsController implements OnDeviceProfileChangeList
return;
}
mProxyView.close(false);
mSharedState.allAppsVisible = false;
onDestroy();
}
/** Destroys the controller and any All Apps window if present. */
public void onDestroy() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
mTaskbarContext.removeOnDeviceProfileChangeListener(this);
Optional.ofNullable(mAllAppsContext)
.map(c -> c.getSystemService(WindowManager.class))
@@ -50,17 +50,21 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllApp
}
/** Opens the all apps view. */
void show() {
void show(boolean animate) {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
}
mIsOpen = true;
attachToContainer();
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
if (animate) {
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
} else {
mTranslationShift = TRANSLATION_SHIFT_OPENED;
}
}
/** The apps container inside this view. */
@@ -53,8 +53,8 @@ final class TaskbarAllAppsViewController {
}
/** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
void show() {
mSlideInView.show();
void show(boolean animate) {
mSlideInView.show(animate);
}
/** Closes the {@link TaskbarAllAppsSlideInView}. */
@@ -26,12 +26,14 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SC
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.util.FloatProperty;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -65,7 +67,10 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
float recentsAlpha = state.overviewUi ? 1f : 0;
Log.d(BAD_STATE, "BaseRecentsViewStateController setState state=" + state
+ ", alpha=" + recentsAlpha);
getContentAlphaProperty().set(mRecentsView, recentsAlpha);
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
RECENTS_GRID_PROGRESS.set(mRecentsView,
state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
@@ -74,6 +79,8 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
@Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation builder) {
Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimation state=" + toState
+ ", config.skipOverview=" + config.hasAnimationFlag(SKIP_OVERVIEW));
if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
return;
}
@@ -97,7 +104,10 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
float recentsAlpha = toState.overviewUi ? 1 : 0;
Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimationInternal toState="
+ toState + ", alpha=" + recentsAlpha);
setter.setFloat(mRecentsView, getContentAlphaProperty(), recentsAlpha,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
setter.setFloat(
@@ -38,6 +38,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_S
import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
@@ -54,6 +55,7 @@ import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.PointF;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
@@ -224,6 +226,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
// Set RecentView's initial properties.
RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators setContentAlpha=1");
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
@@ -242,6 +245,24 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
QUICK_SWITCH.getWorkspaceScrimColor(mLauncher), LINEAR);
if (mRecentsView.getTaskViewCount() == 0) {
xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators from: 0 to: 1");
xAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onStart");
}
@Override
public void onAnimationCancel(Animator animation) {
float alpha = mRecentsView == null ? -1 : CONTENT_ALPHA.get(mRecentsView);
Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onCancel, alpha=" + alpha);
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onEnd");
}
});
}
mXOverviewAnim = xAnim.createPlaybackController();
mXOverviewAnim.dispatchOnStart();
@@ -29,6 +29,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TR
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -36,6 +37,7 @@ import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHO
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
@@ -112,6 +114,7 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
RECENTS_SCALE_PROPERTY.set(mOverviewPanel,
QUICK_SWITCH.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f);
Log.d(BAD_STATE, "QuickSwitchTouchController initCurrentAnimation setContentAlpha=1");
mOverviewPanel.setContentAlpha(1);
mCurrentAnimation = mLauncher.getStateManager()
@@ -0,0 +1,308 @@
/*
* 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;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.BackEvent;
import android.window.IOnBackInvokedCallback;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
/**
* Controls the animation of swiping back and returning to launcher.
*
* This is a two part animation. The first part is an animation that tracks gesture location to
* scale and move the leaving app window. Once the gesture is committed, the second part takes over
* the app window and plays the rest of app close transitions in one go.
*
* This animation is used only for apps that enable back dispatching via
* {@link android.view.OnBackInvokedDispatcher}. The controller registers
* an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back
* navigation to launcher starts.
*
* Apps using the legacy back dispatching will keep triggering the WALLPAPER_OPEN remote
* transition registered in {@link QuickstepTransitionManager}.
*
*/
public class LauncherBackAnimationController {
private static final int CANCEL_TRANSITION_DURATION = 150;
private static final String TAG = "LauncherBackAnimationController";
private final DeviceProfile mDeviceProfile;
private final QuickstepTransitionManager mQuickstepTransitionManager;
private final Matrix mTransformMatrix = new Matrix();
private final RectF mTargetRectF = new RectF();
private final RectF mStartRectF = new RectF();
private final RectF mCurrentRect = new RectF();
private final BaseQuickstepLauncher mLauncher;
private final int mWindowScaleMarginX;
private final int mWindowScaleMarginY;
private final float mWindowScaleEndCornerRadius;
private final float mWindowScaleStartCornerRadius;
private RemoteAnimationTargetCompat mBackTarget;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSpringAnimationInProgress = false;
private boolean mAnimatorSetInProgress = false;
@BackEvent.SwipeEdge
private int mSwipeEdge;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
public LauncherBackAnimationController(
DeviceProfile deviceProfile,
BaseQuickstepLauncher launcher,
QuickstepTransitionManager quickstepTransitionManager) {
mDeviceProfile = deviceProfile;
mLauncher = launcher;
mQuickstepTransitionManager = quickstepTransitionManager;
mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
mLauncher.getResources())
? mLauncher.getResources().getDimensionPixelSize(
R.dimen.swipe_back_window_corner_radius)
: 0;
mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
R.dimen.swipe_back_window_scale_x_margin);
mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
R.dimen.swipe_back_window_scale_y_margin);
}
/**
* Registers {@link IOnBackInvokedCallback} to receive back dispatches from shell.
* @param handler Handler to the thread to run the animations on.
*/
public void registerBackCallbacks(Handler handler) {
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
if (systemUiProxy == null) {
Log.e(TAG, "SystemUiProxy is null. Skip registering back invocation callbacks");
return;
}
systemUiProxy.setBackToLauncherCallback(
new IOnBackInvokedCallback.Stub() {
@Override
public void onBackCancelled() {
handler.post(() -> resetPositionAnimated());
}
@Override
public void onBackInvoked() {
handler.post(() -> startTransition());
}
@Override
public void onBackProgressed(BackEvent backEvent) {
mBackProgress = backEvent.getProgress();
// TODO: Update once the interpolation curve spec is finalized.
mBackProgress =
1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
- mBackProgress);
if (!mBackInProgress) {
startBack(backEvent);
} else {
updateBackProgress(mBackProgress);
}
}
public void onBackStarted() { }
});
}
private void resetPositionAnimated() {
ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
cancelAnimator.addUpdateListener(
animation -> {
updateBackProgress((float) animation.getAnimatedValue());
});
cancelAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finishAnimation();
}
});
cancelAnimator.start();
}
/** Unregisters the back to launcher callback in shell. */
public void unregisterBackCallbacks() {
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
if (systemUiProxy != null) {
systemUiProxy.clearBackToLauncherCallback();
}
}
private void startBack(BackEvent backEvent) {
mBackInProgress = true;
RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
if (appTarget == null) {
return;
}
mTransaction.show(appTarget.leash).apply();
mTransaction.setAnimationTransaction();
mBackTarget = new RemoteAnimationTargetCompat(appTarget);
mSwipeEdge = backEvent.getSwipeEdge();
float screenWidth = mDeviceProfile.widthPx;
float screenHeight = mDeviceProfile.heightPx;
float targetHeight = screenHeight - 2 * mWindowScaleMarginY;
float targetWidth = targetHeight * screenWidth / screenHeight;
float left;
if (mSwipeEdge == BackEvent.EDGE_LEFT) {
left = screenWidth - targetWidth - mWindowScaleMarginX;
} else {
left = mWindowScaleMarginX;
}
float top = mWindowScaleMarginY;
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
mStartRectF.set(0, 0, screenWidth, screenHeight);
mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
}
private void updateBackProgress(float progress) {
if (mBackTarget == null) {
return;
}
mCurrentRect.set(
MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
Rect currentRect = new Rect();
mCurrentRect.round(currentRect);
// Scale the target window to match the currentRectF.
final float scale = mCurrentRect.width() / mStartRectF.width();
mTransformMatrix.reset();
mTransformMatrix.setScale(scale, scale);
mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
Rect startRect = new Rect();
mStartRectF.round(startRect);
float cornerRadius = Utilities.mapRange(
progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
builder.withMatrix(mTransformMatrix)
.withWindowCrop(startRect)
.withCornerRadius(cornerRadius);
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
if (surfaceParams.surface.isValid()) {
surfaceParams.applyTo(mTransaction);
}
mTransaction.apply();
}
private void startTransition() {
if (mBackTarget == null) {
// Trigger transition system instead of custom transition animation.
finishAnimation();
return;
}
if (mLauncher.isDestroyed()) {
return;
}
// TODO: Catch the moment when launcher becomes visible after the top app un-occludes
// launcher and start animating afterwards. Currently we occasionally get a flicker from
// animating when launcher is still invisible.
if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
mLauncher.getStateManager().moveToRestState();
}
Pair<RectFSpringAnim, AnimatorSet> pair =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
new RemoteAnimationTargetCompat[]{mBackTarget},
new RemoteAnimationTargetCompat[]{},
false /* fromUnlock */,
mCurrentRect);
startTransitionAnimations(pair.first, pair.second);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
}
private void finishAnimation() {
mBackTarget = null;
mBackInProgress = false;
mBackProgress = 0;
mSwipeEdge = BackEvent.EDGE_LEFT;
mTransformMatrix.reset();
mTargetRectF.setEmpty();
mCurrentRect.setEmpty();
mStartRectF.setEmpty();
mAnimatorSetInProgress = false;
mSpringAnimationInProgress = false;
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
if (systemUiProxy != null) {
SystemUiProxy.INSTANCE.getNoCreate().onBackToLauncherAnimationFinished();
}
}
private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
mAnimatorSetInProgress = anim != null;
mSpringAnimationInProgress = springAnim != null;
if (springAnim != null) {
springAnim.addAnimatorListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSpringAnimationInProgress = false;
tryFinishBackAnimation();
}
}
);
}
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimatorSetInProgress = false;
tryFinishBackAnimation();
}
});
anim.start();
}
private void tryFinishBackAnimation() {
if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) {
finishAnimation();
}
}
}
@@ -24,6 +24,7 @@ import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSI
import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -38,6 +39,7 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceControl.Transaction;
import android.view.View;
@@ -312,6 +314,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
protected void onStart() {
// Set the alpha to 1 before calling super, as it may get set back to 0 due to
// onActivityStart callback.
Log.d(BAD_STATE, "RecentsActivity onStart mFallbackRecentsView.setContentAlpha(1)");
mFallbackRecentsView.setContentAlpha(1);
super.onStart();
mFallbackRecentsView.updateLocusId();
@@ -39,6 +39,7 @@ import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
@@ -50,6 +51,7 @@ import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -82,6 +84,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
private IShellTransitions mShellTransitions;
private IStartingWindow mStartingWindow;
private IRecentTasks mRecentTasks;
private IBackAnimation mBackAnimation;
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
MAIN_EXECUTOR.execute(() -> clearProxy());
};
@@ -96,6 +99,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
private IOnBackInvokedCallback mBackToLaunchCallback;
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -162,8 +166,8 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController) {
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
IBackAnimation backAnimation) {
unlinkToDeath();
mSystemUiProxy = proxy;
mPip = pip;
@@ -173,6 +177,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
mStartingWindow = startingWindow;
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
mRecentTasks = recentTasks;
mBackAnimation = backAnimation;
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
if (mPipAnimationListener != null && mPip != null) {
@@ -195,6 +200,9 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
if (mBackAnimation != null && mBackToLaunchCallback != null) {
setBackToLauncherCallback(mBackToLaunchCallback);
}
if (mPendingSetNavButtonAlpha != null) {
mPendingSetNavButtonAlpha.run();
@@ -203,7 +211,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
}
public void clearProxy() {
setProxy(null, null, null, null, null, null, null, null);
setProxy(null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -822,6 +830,49 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
mRecentTasksListener = null;
}
//
// Back navigation transitions
//
/** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
mBackToLaunchCallback = callback;
if (mBackAnimation == null) {
return;
}
try {
mBackAnimation.setBackToLauncherCallback(callback);
} catch (RemoteException e) {
Log.e(TAG, "Failed call setBackToLauncherCallback", e);
}
}
/** Clears the previously registered {@link IOnBackInvokedCallback}. */
public void clearBackToLauncherCallback() {
if (mBackAnimation == null) {
return;
}
try {
mBackAnimation.clearBackToLauncherCallback();
} catch (RemoteException e) {
Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
}
}
/**
* Notifies shell that all back to launcher animations have finished (including the transition
* that plays after the gesture is committed and before the app is closed.
*/
public void onBackToLauncherAnimationFinished() {
if (mBackAnimation != null) {
try {
mBackAnimation.onBackToLauncherAnimationFinished();
} catch (RemoteException e) {
Log.w(TAG, "Failed call onBackAnimationFinished", e);
}
}
}
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
if (mRecentTasks != null) {
try {
@@ -37,6 +37,7 @@ import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLAT
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -55,6 +56,7 @@ import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.View;
import android.window.TransitionInfo;
@@ -577,6 +579,29 @@ public final class TaskViewUtils {
launcherAnim = dp.isTablet
? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
: recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
if (dp.isTablet) {
Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator alpha=" + 0);
launcherAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onStart");
}
@Override
public void onAnimationCancel(Animator animation) {
float alpha = recentsView == null
? -1
: RecentsView.CONTENT_ALPHA.get(recentsView);
Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onCancel, alpha="
+ alpha);
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onEnd");
}
});
}
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
@@ -26,6 +26,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -106,6 +107,7 @@ import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -166,10 +168,12 @@ public class TouchInteractionService extends Service
bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
launcherUnlockAnimationController);
launcherUnlockAnimationController, backAnimation);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
});
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -27,6 +28,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
@@ -221,6 +223,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
setOverviewStateEnabled(true);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.isFullScreen());
Log.d(BAD_STATE, "FRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
setFreezeViewVisibility(true);
}
@@ -232,6 +235,8 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
}
boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
Log.d(BAD_STATE, "FRV onStateTransitionComplete setFreezeVisibility=false, finalState="
+ finalState);
setFreezeViewVisibility(false);
if (isOverlayEnabled) {
@@ -64,7 +64,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
private static final String TAG = "TutorialController";
private static final float FINGER_DOT_VISIBLE_ALPHA = 0.6f;
private static final float FINGER_DOT_VISIBLE_ALPHA = 0.7f;
private static final float FINGER_DOT_SMALL_SCALE = 0.7f;
private static final int FINGER_DOT_ANIMATION_DURATION_MILLIS = 500;
@@ -15,10 +15,13 @@
*/
package com.android.quickstep.util;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.util.Log;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -27,6 +30,8 @@ import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.views.RecentsView;
import java.util.Arrays;
public class RecentsAtomicAnimationFactory<ACTIVITY_TYPE extends StatefulActivity, STATE_TYPE>
extends AtomicAnimationFactory<STATE_TYPE> {
@@ -46,8 +51,30 @@ public class RecentsAtomicAnimationFactory<ACTIVITY_TYPE extends StatefulActivit
public Animator createStateElementAnimation(int index, float... values) {
switch (index) {
case INDEX_RECENTS_FADE_ANIM:
return ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
ObjectAnimator alpha = ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
Log.d(BAD_STATE, "RAAF createStateElementAnimation alpha="
+ Arrays.toString(values));
alpha.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
Log.d(BAD_STATE, "RAAF createStateElementAnimation onStart");
}
@Override
public void onAnimationCancel(Animator animation) {
RecentsView recent = mActivity.getOverviewPanel();
float alpha = recent == null ? -1 : RecentsView.CONTENT_ALPHA.get(recent);
Log.d(BAD_STATE, "RAAF createStateElementAnimation onCancel, alpha="
+ alpha);
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d(BAD_STATE, "RAAF createStateElementAnimation onEnd");
}
});
return alpha;
case INDEX_RECENTS_TRANSLATE_X_ANIM: {
RecentsView rv = mActivity.getOverviewPanel();
return new SpringAnimationBuilder(mActivity)
@@ -21,11 +21,13 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
@@ -65,6 +67,7 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun
public void init(OverviewActionsView actionsView,
SplitSelectStateController splitPlaceholderView) {
super.init(actionsView, splitPlaceholderView);
Log.d(BAD_STATE, "LauncherRecentsView init setContentAlpha=0");
setContentAlpha(0);
}
@@ -97,6 +100,7 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun
setOverviewStateEnabled(toState.overviewUi);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
Log.d(BAD_STATE, "LRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
setFreezeViewVisibility(true);
}
@@ -108,6 +112,8 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun
}
boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
Log.d(BAD_STATE, "LRV onStateTransitionComplete setFreezeVisibility=false, finalState="
+ finalState);
setFreezeViewVisibility(false);
if (isOverlayEnabled) {
@@ -0,0 +1,155 @@
/*
* 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;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static junit.framework.TestCase.assertEquals;
import android.content.Intent;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.tapl.Taskbar;
import com.android.launcher3.ui.TaplTestsLauncher3;
import org.junit.After;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class TaplTestsTaskbar extends AbstractQuickStepTest {
private static final String TEST_APP_NAME = "LauncherTestApp";
private static final String TEST_APP_PACKAGE =
getInstrumentation().getContext().getPackageName();
private static final String CALCULATOR_APP_PACKAGE =
resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
@Override
public void setUp() throws Exception {
Assume.assumeTrue(mLauncher.isTablet());
super.setUp();
mLauncher.useTestWorkspaceLayoutOnReload();
TaplTestsLauncher3.initialize(this);
startAppFast(CALCULATOR_APP_PACKAGE);
mLauncher.showTaskbarIfHidden();
}
@After
public void tearDown() {
mLauncher.useDefaultWorkspaceLayoutOnReload();
}
@Test
public void testHideShowTaskbar() {
getTaskbar().hide();
mLauncher.getLaunchedAppState().showTaskbar();
}
@Test
public void testLaunchApp() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
}
@Test
public void testOpenMenu() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
}
@Test
public void testLaunchShortcut() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME)
.openDeepShortcutMenu()
.getMenuItem("Shortcut 1")
.launch(TEST_APP_PACKAGE);
}
@Test
@PortraitLandscape
public void testLaunchAppInSplitscreen() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
}
@Test
@PortraitLandscape
public void testLaunchShortcutInSplitscreen() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME)
.openDeepShortcutMenu()
.getMenuItem("Shortcut 1")
.dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
}
@Test
public void testLaunchApp_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
}
@Test
public void testOpenMenu_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
}
@Test
public void testLaunchShortcut_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
.openDeepShortcutMenu()
.getMenuItem("Shortcut 1")
.launch(TEST_APP_PACKAGE);
}
@Test
@PortraitLandscape
public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
.dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
}
@Test
@PortraitLandscape
public void testLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
getTaskbar().openAllApps()
.getAppIcon(TEST_APP_NAME)
.openDeepShortcutMenu()
.getMenuItem("Shortcut 1")
.dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
}
private Taskbar getTaskbar() {
Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
List<String> taskbarIconNames = taskbar.getIconNames();
List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
assertEquals("Taskbar and hotseat icon counts do not match",
taskbarIconNames.size(), hotseatIconNames.size());
for (int i = 0; i < taskbarIconNames.size(); i++) {
assertEquals("Taskbar and Hotseat icons do not match",
taskbarIconNames, hotseatIconNames);
}
return taskbar;
}
}
+4
View File
@@ -166,4 +166,8 @@
<!-- Name of the class used to generate colors from the wallpaper colors. Must be implementing the LauncherAppWidgetHostView.ColorGenerator interface. -->
<string name="color_generator_class" translatable="false"/>
<!-- Swipe back to home related -->
<dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
<dimen name="swipe_back_window_scale_y_margin">80dp</dimen>
<dimen name="swipe_back_window_corner_radius">40dp</dimen>
</resources>
+29
View File
@@ -0,0 +1,29 @@
<?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.
-->
<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
<!-- Launcher Test Activity -->
<resolve
launcher:container="-101"
launcher:screen="0"
launcher:x="0"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.CATEGORY_TEST;component=com.google.android.apps.nexuslauncher.tests/com.android.launcher3.testcomponent.BaseTestingActivity;end" />
</resolve>
</favorites>
+27
View File
@@ -57,6 +57,7 @@ import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import android.animation.Animator;
@@ -501,6 +502,8 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
Log.d(BAD_STATE, "Launcher onCreate not binding sync, setting DragLayer alpha "
+ "ALPHA_INDEX_LAUNCHER_LOAD to 0");
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
@@ -519,6 +522,8 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
final AlphaProperty property =
mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
if (property.getValue() == 0) {
Log.d(BAD_STATE, "Launcher onPreDraw ALPHA_INDEX_LAUNCHER_LOAD not"
+ " started yet, cancelling draw.");
// Animation haven't started yet; suspend.
return false;
} else {
@@ -2685,6 +2690,28 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
AlphaProperty property = mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
if (property.getValue() < 1) {
ObjectAnimator anim = ObjectAnimator.ofFloat(property, MultiValueAlpha.VALUE, 1);
Log.d(BAD_STATE, "Launcher onInitialBindComplete toAlpha=" + 1);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
Log.d(BAD_STATE, "Launcher onInitialBindComplete onStart");
}
@Override
public void onAnimationCancel(Animator animation) {
float alpha = mDragLayer == null
? -1
: mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).getValue();
Log.d(BAD_STATE, "Launcher onInitialBindComplete onCancel, alpha=" + alpha);
}
@Override
public void onAnimationEnd(Animator animation) {
Log.d(BAD_STATE, "Launcher onInitialBindComplete onEnd");
}
});
anim.addListener(AnimatorListeners.forEndCallback(executor::onLoadAnimationCompleted));
anim.start();
} else {
@@ -102,6 +102,8 @@ public class LauncherProvider extends ContentProvider {
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
protected DatabaseHelper mOpenHelper;
@@ -109,6 +111,8 @@ public class LauncherProvider extends ContentProvider {
private long mLastRestoreTimestamp = 0L;
private boolean mUseTestWorkspaceLayout;
/**
* $ adb shell dumpsys activity provider com.android.launcher3
*/
@@ -390,6 +394,14 @@ public class LauncherProvider extends ContentProvider {
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
return null;
}
case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
mUseTestWorkspaceLayout = true;
return null;
}
case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
mUseTestWorkspaceLayout = false;
return null;
}
case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
loadDefaultFavoritesIfNecessary();
return null;
@@ -609,7 +621,8 @@ public class LauncherProvider extends ContentProvider {
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
int defaultLayout = idp.defaultLayoutId;
int defaultLayout = mUseTestWorkspaceLayout
? TEST_WORKSPACE_LAYOUT_RES_XML : idp.defaultLayoutId;
if (getContext().getSystemService(UserManager.class).isDemoUser()
&& idp.demoModeLayoutId != 0) {
@@ -374,6 +374,12 @@ public class LauncherSettings {
public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
public static final String METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG =
"set_use_test_workspace_layout_flag";
public static final String METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG =
"clear_use_test_workspace_layout_flag";
public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
+2
View File
@@ -30,6 +30,7 @@ import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -1266,6 +1267,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
// different effects based on device performance. On at least one relatively high-end
// device I've tried, translating the launcher causes things to get quite laggy.
mLauncher.getDragLayer().setTranslationX(transX);
Log.d(BAD_STATE, "Workspace onOverlayScrollChanged DragLayer ALPHA_INDEX_OVERLAY=" + alpha);
mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
}
@@ -442,8 +442,8 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
if (showTabs == mUsingTabs && !force) {
return;
}
replaceRVContainer(showTabs);
mUsingTabs = showTabs;
replaceRVContainer(mUsingTabs);
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
@@ -100,14 +100,12 @@ public class AllAppsSearchBarController
@Override
public void afterTextChanged(final Editable s) {
mQuery = s.toString();
mInput.getSelectionStart();
if (mQuery.isEmpty()) {
mSearchAlgorithm.cancel(true);
mCallback.clearSearchResult();
} else {
mSearchAlgorithm.cancel(false);
mSearchAlgorithm.doSearch(mQuery, mInput.getSelectionStart(), mTextConversions,
mCallback);
mSearchAlgorithm.doSearch(mQuery, mTextConversions, mCallback);
}
}
@@ -117,7 +115,7 @@ public class AllAppsSearchBarController
}
// If play store continues auto updating an app, we want to show partial result.
mSearchAlgorithm.cancel(false);
mSearchAlgorithm.doSearch(mQuery, mInput.getSelectionStart(), mCallback);
mSearchAlgorithm.doSearch(mQuery, mCallback);
}
@Override
@@ -58,7 +58,7 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> {
}
@Override
public void doSearch(String query, int cursorLocation, SearchCallback<AdapterItem> callback) {
public void doSearch(String query, SearchCallback<AdapterItem> callback) {
mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
@@ -98,6 +98,7 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram
final ValueAnimator mAnim;
// Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends.
private boolean mAnimStarted;
private Runnable mOnAnimEndCallback = null;
private int mLastTouchX;
private int mLastTouchY;
@@ -180,6 +181,14 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram
public void onAnimationStart(Animator animation) {
mAnimStarted = true;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mOnAnimEndCallback != null) {
mOnAnimEndCallback.run();
}
}
});
setDragRegion(new Rect(0, 0, width, height));
@@ -199,6 +208,10 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram
setWillNotDraw(false);
}
public void setOnAnimationEndCallback(Runnable callback) {
mOnAnimEndCallback = callback;
}
/**
* Initialize {@code #mIconDrawable} if the item can be represented using
* an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
@@ -25,14 +25,13 @@ public interface SearchAlgorithm<T> {
/**
* Performs search and sends the result to {@link SearchCallback}.
*/
void doSearch(String query, int cursorLocation, SearchCallback<T> callback);
void doSearch(String query, SearchCallback<T> callback);
/**
* Performs search with {@code query} and the {@code suggestedQueries}/
*/
default void doSearch(String query, int cursorLocation, String[] suggestedQueries,
SearchCallback<T> callback) {
doSearch(query, cursorLocation, callback);
default void doSearch(String query, String[] suggestedQueries, SearchCallback<T> callback) {
doSearch(query, callback);
}
/**
@@ -82,6 +82,10 @@ public final class TestProtocol {
public static final String REQUEST_IS_LAUNCHER_INITIALIZED = "is-launcher-initialized";
public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing";
public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing";
public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
@@ -96,6 +100,10 @@ public final class TestProtocol {
public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
public static final String REQUEST_CLEAR_DATA = "clear-data";
public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
"use-default-workspace-layout";
public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
public static final String REQUEST_IS_TABLET = "is-tablet";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
@@ -128,4 +136,6 @@ public final class TestProtocol {
public static final String NO_DROP_TARGET = "b/195031154";
public static final String NULL_INT_SET = "b/200572078";
public static final String MISSING_PROMISE_ICON = "b/202985412";
public static final String BAD_STATE = "b/223498680";
}
@@ -48,8 +48,7 @@ public final class SimpleWidgetsSearchAlgorithm implements SearchAlgorithm<Widge
}
@Override
public void doSearch(String query, int cursorLocation,
SearchCallback<WidgetsListBaseEntry> callback) {
public void doSearch(String query, SearchCallback<WidgetsListBaseEntry> callback) {
ArrayList<WidgetsListBaseEntry> result = getFilteredWidgets(mDataProvider, query);
mResultHandler.post(() -> callback.onSearchResult(query, result));
}
@@ -71,7 +71,7 @@ public class WidgetsSearchBarController implements TextWatcher,
} else {
mSearchAlgorithm.cancel(/* interruptActiveRequests= */ false);
mSearchModeListener.enterSearchMode();
mSearchAlgorithm.doSearch(mQuery, mInput.getSelectionStart(), this);
mSearchAlgorithm.doSearch(mQuery, this);
mCancelButton.setVisibility(VISIBLE);
}
}
+1 -1
View File
@@ -94,7 +94,6 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
@@ -138,6 +137,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.android.launcher3.intent.action.test_shortcut"/>
@@ -567,6 +567,7 @@ public abstract class AbstractLauncherUiTest {
ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
break;
}
case TASKBAR_ALL_APPS:
case LAUNCHED_APP: {
assertTrue("Launcher is resumed in state: " + expectedContainerType,
!isResumed);
@@ -580,9 +581,10 @@ public abstract class AbstractLauncherUiTest {
}
} else {
assertTrue(
"Container type is not LAUNCHED_APP or FALLBACK_OVERVIEW: "
+ expectedContainerType,
"Container type is not LAUNCHED_APP, TASKBAR_ALL_APPS "
+ "or FALLBACK_OVERVIEW: " + expectedContainerType,
expectedContainerType == ContainerType.LAUNCHED_APP
|| expectedContainerType == ContainerType.TASKBAR_ALL_APPS
|| expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
}
}
@@ -163,7 +163,7 @@ public class SimpleWidgetsSearchAlgorithmTest {
mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
.when(mDataProvider)
.getAllWidgets();
mSimpleWidgetsSearchAlgorithm.doSearch("Ca", 2, mSearchCallback);
mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
MAIN_EXECUTOR.submit(() -> { }).get();
verify(mSearchCallback).onSearchResult(
matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
@@ -86,7 +86,7 @@ public class WidgetsSearchBarControllerTest {
public void afterTextChanged_shouldDoSearch() {
mEditText.setText("abc");
verify(mSearchAlgorithm).doSearch(eq("abc"), 3, any());
verify(mSearchAlgorithm).doSearch(eq("abc"), any());
}
@Test
@@ -74,7 +74,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height");
return false;
}
if (iconCenterInSearchBox(allAppsContainer, icon)) {
if (hasSearchBox() && iconCenterInSearchBox(allAppsContainer, icon)) {
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
@@ -107,7 +107,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
final UiObject2 allAppsContainer = verifyActiveContainer();
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
final UiObject2 searchBox = getSearchBox(allAppsContainer);
final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
int deviceHeight = mLauncher.getRealDisplaySize().y;
int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
@@ -128,8 +128,10 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
mLauncher.getVisibleBounds(icon).top
< bottomGestureStartOnScreen)
.collect(Collectors.toList()),
mLauncher.getVisibleBounds(searchBox).bottom
- mLauncher.getVisibleBounds(allAppsContainer).top);
hasSearchBox()
? mLauncher.getVisibleBounds(searchBox).bottom
- mLauncher.getVisibleBounds(allAppsContainer).top
: 0);
verifyActiveContainer();
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
@@ -173,18 +175,21 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
return appIcon;
}
@NonNull
protected abstract AppIcon createAppIcon(UiObject2 icon);
protected abstract boolean hasSearchBox();
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
LauncherInstrumentation.log("Scrolling to the beginning");
final UiObject2 allAppsContainer = verifyActiveContainer();
final UiObject2 searchBox = getSearchBox(allAppsContainer);
final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
int attempts = 0;
final Rect margins =
new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5);
final Rect margins = new Rect(
0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
for (int scroll = getAllAppsScroll();
scroll != 0;
@@ -196,7 +201,11 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
++attempts <= MAX_SCROLL_ATTEMPTS);
mLauncher.scroll(
allAppsContainer, Direction.UP, margins, 12, false);
allAppsContainer,
Direction.UP,
margins,
/* steps= */ 12,
/* slowDown= */ false);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -225,7 +234,11 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
mLauncher.scroll(
allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10, false);
allAppsContainer,
Direction.DOWN,
new Rect(0, 0, 0, mHeight / 2),
/* steps= */ 10,
/* slowDown= */ false);
verifyActiveContainer();
}
}
@@ -240,7 +253,11 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
mLauncher.scroll(
allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10, false);
allAppsContainer,
Direction.UP,
new Rect(0, mHeight / 2, 0, 0),
/* steps= */ 10,
/*slowDown= */ false);
verifyActiveContainer();
}
}
@@ -0,0 +1,51 @@
/*
* 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.tapl;
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
/**
* Operations on AllApps opened from the Taskbar.
*/
public class AllAppsFromTaskbar extends AllApps {
AllAppsFromTaskbar(LauncherInstrumentation launcher) {
super(launcher);
}
@Override
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.TASKBAR_ALL_APPS;
}
@NonNull
@Override
public TaskbarAppIcon getAppIcon(String appName) {
return (TaskbarAppIcon) super.getAppIcon(appName);
}
@NonNull
@Override
protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
return new TaskbarAppIcon(mLauncher, icon);
}
@Override
protected boolean hasSearchBox() {
return false;
}
}
@@ -51,7 +51,7 @@ public abstract class AppIcon extends Launchable {
public AppIconMenu openMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return createMenu(mLauncher.clickAndGet(
mObject, "popup_container", getLongClickEvent()));
mObject, /* resName= */ "popup_container", getLongClickEvent()));
}
}
@@ -61,7 +61,7 @@ public abstract class AppIcon extends Launchable {
public AppIconMenu openDeepShortcutMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return createMenu(mLauncher.clickAndGet(
mObject, "deep_shortcuts_container", getLongClickEvent()));
mObject, /* resName= */ "deep_shortcuts_container", getLongClickEvent()));
}
}
@@ -73,8 +73,8 @@ public abstract class AppIcon extends Launchable {
}
@Override
protected String getLongPressIndicator() {
return "popup_container";
protected void waitForLongPressConfirmation() {
mLauncher.waitForLauncherObject("popup_container");
}
@Override
@@ -41,8 +41,8 @@ public abstract class AppIconMenuItem extends Launchable {
}
@Override
protected String getLongPressIndicator() {
return "drop_target_bar";
protected void waitForLongPressConfirmation() {
mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
@@ -35,8 +35,14 @@ public class HomeAllApps extends AllApps {
return (AllAppsAppIcon) super.getAppIcon(appName);
}
@NonNull
@Override
protected HomeAppIcon createAppIcon(UiObject2 icon) {
return new AllAppsAppIcon(mLauncher, icon);
}
@Override
protected boolean hasSearchBox() {
return true;
}
}
@@ -51,8 +51,7 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
() -> {
final Rect bounds = target.getDropLocationBounds();
return new Point(bounds.centerX(), bounds.centerY());
},
getLongPressIndicator());
});
FolderIcon result = target.getTargetFolder(dropBounds);
mLauncher.assertTrue("Can't find the target folder.", result != null);
return result;
@@ -115,8 +114,12 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))
) {
final Supplier<Point> dest = () -> Workspace.getCellCenter(mLauncher, cellX, cellY);
Workspace.dragIconToWorkspace(mLauncher, this, dest, getLongPressIndicator(),
() -> addExpectedEventsForLongClick(), null);
Workspace.dragIconToWorkspace(
mLauncher,
/* launchable= */ this,
dest,
() -> addExpectedEventsForLongClick(),
/*expectDropEvents= */ null);
try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) {
WorkspaceAppIcon appIcon =
(WorkspaceAppIcon) mLauncher.getWorkspace().getWorkspaceAppIcon(mAppName);
@@ -97,8 +97,7 @@ abstract class Launchable {
return new LaunchedAppState(mLauncher);
}
Point startDrag(long downTime, String longPressIndicator,
Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
Point startDrag(long downTime, Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
final Point iconCenter = getObject().getVisibleCenter();
final Point dragStartCenter = new Point(iconCenter.x,
iconCenter.y - getStartDragThreshold());
@@ -108,7 +107,6 @@ abstract class Launchable {
downTime,
iconCenter,
dragStartCenter,
longPressIndicator,
expectLongClickEvents),
SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
} else {
@@ -116,7 +114,6 @@ abstract class Launchable {
downTime,
iconCenter,
dragStartCenter,
longPressIndicator,
expectLongClickEvents);
}
@@ -124,22 +121,44 @@ abstract class Launchable {
return dragStartCenter;
}
/**
* Waits for a confirmation that a long press has successfully been triggered.
*
* This method waits for a view to either appear or disappear to confirm that the long press
* has been triggered and fails if no confirmation is received before the default timeout.
*/
protected abstract void waitForLongPressConfirmation();
/**
* Drags this Launchable a short distance before starting a full drag.
*
* This is necessary for shortcuts, which require being dragged beyond a threshold to close
* their container and start drag callbacks.
*/
private void movePointerForStartDrag(long downTime, Point iconCenter, Point dragStartCenter,
String longPressIndicator, Runnable expectLongClickEvents) {
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
iconCenter, LauncherInstrumentation.GestureScope.INSIDE);
private void movePointerForStartDrag(
long downTime,
Point iconCenter,
Point dragStartCenter,
Runnable expectLongClickEvents) {
mLauncher.sendPointer(
downTime,
downTime,
MotionEvent.ACTION_DOWN,
iconCenter,
LauncherInstrumentation.GestureScope.INSIDE);
LauncherInstrumentation.log("movePointerForStartDrag: sent down");
expectLongClickEvents.run();
mLauncher.waitForLauncherObject(longPressIndicator);
waitForLongPressConfirmation();
LauncherInstrumentation.log("movePointerForStartDrag: indicator");
mLauncher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false,
downTime, downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
mLauncher.movePointer(
iconCenter,
dragStartCenter,
DEFAULT_DRAG_STEPS,
/* isDecelerating= */ false,
downTime,
downTime,
/* slowDown= */ true,
LauncherInstrumentation.GestureScope.INSIDE);
}
private int getStartDragThreshold() {
@@ -148,6 +167,4 @@ abstract class Launchable {
}
protected abstract void addExpectedEventsForLongClick();
protected abstract String getLongPressIndicator();
}
@@ -16,11 +16,27 @@
package com.android.launcher3.tapl;
import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.MotionEvent;
import androidx.test.uiautomator.By;
import com.android.launcher3.testing.TestProtocol;
/**
* Background state operations specific to when an app has been launched.
*/
public final class LaunchedAppState extends Background {
// More drag steps than Launchables to give the window manager time to register the drag.
private static final int DEFAULT_DRAG_STEPS = 35;
LaunchedAppState(LauncherInstrumentation launcher) {
super(launcher);
}
@@ -29,4 +45,126 @@ public final class LaunchedAppState extends Background {
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.LAUNCHED_APP;
}
/**
* Returns the taskbar.
*
* The taskbar must already be visible when calling this method.
*/
public Taskbar getTaskbar() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get the taskbar")) {
mLauncher.waitForLauncherObject("taskbar_view");
return new Taskbar(mLauncher);
}
}
/**
* Returns the Taskbar in a visible state.
*
* The taskbar must already be hidden when calling this method.
*/
public Taskbar showTaskbar() {
mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"want to show the taskbar")) {
mLauncher.waitUntilLauncherObjectGone("taskbar_view");
final long downTime = SystemClock.uptimeMillis();
final int unstashTargetY = mLauncher.getRealDisplaySize().y
- (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2);
final Point unstashTarget = new Point(
mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
LauncherInstrumentation.log("showTaskbar: sent down");
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
mLauncher.waitForLauncherObject("taskbar_view");
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
return new Taskbar(mLauncher);
}
} finally {
mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
}
}
static void dragToSplitscreen(
LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
String expectedExistingPackageName) {
try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
"want to drag taskbar item to splitscreen")) {
final Point displaySize = launcher.getRealDisplaySize();
final Point endPoint = new Point(displaySize.x / 4, 3 * displaySize.y / 4);
final long downTime = SystemClock.uptimeMillis();
// Use mObject before starting drag since the system drag and drop moves the original
// view.
Point itemVisibleCenter = launchable.mObject.getVisibleCenter();
Rect itemVisibleBounds = launcher.getVisibleBounds(launchable.mObject);
String itemLabel = launchable.mObject.getText();
Point dragStart = launchable.startDrag(
downTime,
launchable::addExpectedEventsForLongClick,
/* runToSpringLoadedState= */ false);
try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer(
"started item drag")) {
launcher.movePointer(
dragStart,
endPoint,
DEFAULT_DRAG_STEPS,
/* isDecelerating= */ true,
downTime,
SystemClock.uptimeMillis(),
/* slowDown= */ false,
LauncherInstrumentation.GestureScope.INSIDE);
try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer(
"moved pointer to drop point")) {
dropDraggedItem(
launcher,
launchable,
expectedNewPackageName,
endPoint, downTime,
itemVisibleCenter,
itemVisibleBounds,
itemLabel,
expectedExistingPackageName);
}
}
}
}
private static void dropDraggedItem(
LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
Point endPoint, long downTime, Point itemVisibleCenter, Rect itemVisibleBounds,
String itemLabel, String expectedExistingPackageName) {
LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen before drop "
+ itemVisibleCenter + " in " + itemVisibleBounds);
launchable.executeAndWaitForWindowChange(() -> {
launcher.sendPointer(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP,
endPoint,
LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER);
LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: after "
+ "drop");
}, itemLabel, "dropping taskbar item");
try (LauncherInstrumentation.Closable c = launcher.addContextLayer("dropped item")) {
launchable.assertAppLaunched(itemLabel, By.pkg(expectedNewPackageName));
launcher.checkPackagesVisible(
new String[] {expectedNewPackageName, expectedExistingPackageName});
}
}
}
@@ -84,6 +84,7 @@ import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
@@ -123,7 +124,8 @@ public final class LauncherInstrumentation {
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP
WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP,
TASKBAR_ALL_APPS
}
public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
@@ -167,6 +169,7 @@ public final class LauncherInstrumentation {
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "popup_container";
private static final String TASKBAR_RES_ID = "taskbar_view";
public static final int WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -500,6 +503,16 @@ public final class LauncherInstrumentation {
}
}
void checkPackagesVisible(String[] expectedVisiblePackages) {
Set<String> actualVisiblePackages =
getVisiblePackagesStream().collect(Collectors.toSet());
for (String expectedVisible : expectedVisiblePackages) {
assertTrue("Expected package not visible: " + expectedVisible,
actualVisiblePackages.contains(expectedVisible));
}
}
private String getVisiblePackages() {
final String apps = getVisiblePackagesStream().collect(Collectors.joining(", "));
return !apps.isEmpty()
@@ -722,27 +735,41 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(WORKSPACE_RES_ID);
}
case WIDGETS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(WIDGETS_RES_ID);
}
case TASKBAR_ALL_APPS:
case HOME_ALL_APPS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(OVERVIEW_RES_ID);
}
case FALLBACK_OVERVIEW: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
}
case LAUNCHED_APP: {
@@ -750,6 +777,12 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
if (isTablet() && !isFallbackOverview()) {
waitForLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilLauncherObjectGone(TASKBAR_RES_ID);
}
return null;
}
default:
@@ -863,8 +896,13 @@ public final class LauncherInstrumentation {
setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
final Point displaySize = getRealDisplaySize();
// The swipe up to home gesture starts from inside the launcher when the user is
// already home. Otherwise, the gesture can start inside the launcher process if the
// taskbar is visible.
boolean gestureStartFromLauncher = isTablet()
? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
? !isLauncher3()
|| hasLauncherObject(WORKSPACE_RES_ID)
|| hasLauncherObject(TASKBAR_RES_ID)
: isLauncherVisible();
// CLose floating views before going back to home.
@@ -1301,9 +1339,7 @@ public final class LauncherInstrumentation {
}
void scrollToLastVisibleRow(
UiObject2 container,
Collection<UiObject2> items,
int topPaddingInContainer) {
UiObject2 container, Collection<UiObject2> items, int topPaddingInContainer) {
final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top));
@@ -1326,8 +1362,8 @@ public final class LauncherInstrumentation {
containerRect.height() - distance - bottomGestureMarginInContainer,
0,
bottomGestureMarginInContainer),
10,
true);
/* steps= */ 10,
/* slowDown= */ true);
}
void scrollLeftByDistance(UiObject2 container, int distance) {
@@ -1650,6 +1686,29 @@ public final class LauncherInstrumentation {
getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
}
/**
* Reloads the workspace with a test layout that includes the Test Activity app icon on the
* hotseat.
*/
public void useTestWorkspaceLayoutOnReload() {
getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
}
/** Reloads the workspace with the default layout defined by the user's grid size selection. */
public void useDefaultWorkspaceLayoutOnReload() {
getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
}
/** Shows the taskbar if it is hidden, otherwise does nothing. */
public void showTaskbarIfHidden() {
getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);
}
public List<String> getHotseatIconNames() {
return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
.getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
private String[] getActivities() {
return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES)
.getStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -0,0 +1,39 @@
/*
* 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.tapl;
/** Launchable that can serve as a source for dragging and dropping to splitscreen. */
interface SplitscreenDragSource {
/**
* Drags this app icon to the left (landscape) or bottom (portrait) of the screen, launching it
* in splitscreen.
*
* @param expectedNewPackageName package name of the app being dragged
* @param expectedExistingPackageName package name of the already-launched app
*/
default void dragToSplitscreen(
String expectedNewPackageName, String expectedExistingPackageName) {
Launchable launchable = getLaunchable();
LauncherInstrumentation launcher = launchable.mLauncher;
try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
LaunchedAppState.dragToSplitscreen(
launcher, launchable, expectedNewPackageName, expectedExistingPackageName);
}
}
Launchable getLaunchable();
}
@@ -0,0 +1,123 @@
/*
* 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.tapl;
import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import android.graphics.Point;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
import java.util.List;
import java.util.stream.Collectors;
/**
* Operations on the Taskbar from LaunchedApp.
*/
public final class Taskbar {
private final LauncherInstrumentation mLauncher;
Taskbar(LauncherInstrumentation launcher) {
mLauncher = launcher;
}
/**
* Returns an app icon with the given name. This fails if the icon is not found.
*/
@NonNull
public TaskbarAppIcon getAppIcon(String appName) {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get a taskbar icon")) {
return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
mLauncher.waitForLauncherObject("taskbar_view"),
AppIcon.getAppIconSelector(appName, mLauncher)));
}
}
/**
* Hides this taskbar.
*
* The taskbar must already be visible when calling this method.
*/
public void hide() {
mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to hide the taskbar");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.waitForLauncherObject("taskbar_view");
final long downTime = SystemClock.uptimeMillis();
Point stashTarget = new Point(
mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
LauncherInstrumentation.GestureScope.INSIDE);
LauncherInstrumentation.log("hideTaskbar: sent down");
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
mLauncher.waitUntilLauncherObjectGone("taskbar_view");
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
LauncherInstrumentation.GestureScope.INSIDE);
}
} finally {
mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
}
}
/**
* Opens the Taskbar all apps page.
*/
public AllAppsFromTaskbar openAllApps() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to open taskbar all apps");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
mLauncher.waitForLauncherObject("taskbar_view"), getAllAppsButtonSelector()));
return new AllAppsFromTaskbar(mLauncher);
}
}
/** Returns a list of app icon names on the Taskbar */
public List<String> getIconNames() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get all taskbar icons")) {
return mLauncher.waitForObjectsInContainer(
mLauncher.waitForLauncherObject("taskbar_view"),
AppIcon.getAnyAppIconSelector())
.stream()
.map(UiObject2::getText)
.filter(text -> !TextUtils.isEmpty(text)) // Filter out the all apps button
.collect(Collectors.toList());
}
}
private static BySelector getAllAppsButtonSelector() {
// Look for an icon with no text
return By.clazz(TextView.class).text("");
}
}
@@ -0,0 +1,52 @@
/*
* 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.tapl;
import androidx.test.uiautomator.UiObject2;
import java.util.regex.Pattern;
/**
* App icon specifically on the Taskbar.
*/
public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSource {
private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
TaskbarAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
}
@Override
protected Pattern getLongClickEvent() {
return LONG_CLICK_EVENT;
}
@Override
public TaskbarAppIconMenu openDeepShortcutMenu() {
return (TaskbarAppIconMenu) super.openDeepShortcutMenu();
}
@Override
protected TaskbarAppIconMenu createMenu(UiObject2 menu) {
return new TaskbarAppIconMenu(mLauncher, menu);
}
@Override
public Launchable getLaunchable() {
return this;
}
}
@@ -0,0 +1,38 @@
/*
* 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.tapl;
import androidx.test.uiautomator.UiObject2;
/**
* Context menu of a Taskbar app icon.
*/
public final class TaskbarAppIconMenu extends AppIconMenu {
TaskbarAppIconMenu(LauncherInstrumentation launcher, UiObject2 deepShortcutsContainer) {
super(launcher, deepShortcutsContainer);
}
@Override
public TaskbarAppIconMenuItem getMenuItem(String shortcutText) {
return (TaskbarAppIconMenuItem) super.getMenuItem(shortcutText);
}
@Override
protected TaskbarAppIconMenuItem createMenuItem(UiObject2 menuItem) {
return new TaskbarAppIconMenuItem(mLauncher, menuItem);
}
}
@@ -0,0 +1,57 @@
/*
* 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.tapl;
import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.TestProtocol;
import java.util.regex.Pattern;
/**
* Menu item in a Taskbar app icon menu.
*/
public final class TaskbarAppIconMenuItem extends AppIconMenuItem implements SplitscreenDragSource {
private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
TaskbarAppIconMenuItem(
LauncherInstrumentation launcher, UiObject2 shortcut) {
super(launcher, shortcut);
}
@Override
protected void addExpectedEventsForLongClick() {
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
}
@Override
protected void waitForLongPressConfirmation() {
// On long-press, the popup container closes and the system drag-and-drop begins. This
// only leaves launcher views that were previously visible.
mLauncher.waitUntilLauncherObjectGone("popup_container");
}
@Override
protected String launchableType() {
return "taskbar app icon menu item";
}
@Override
public Launchable getLaunchable() {
return this;
}
}
@@ -39,8 +39,8 @@ public final class Widget extends Launchable implements WorkspaceDragSource {
}
@Override
protected String getLongPressIndicator() {
return "drop_target_bar";
protected void waitForLongPressConfirmation() {
mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
@@ -205,7 +205,6 @@ public final class Workspace extends Home {
mLauncher,
homeAppIcon,
new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
"popup_container",
false,
false,
() -> mLauncher.expectEvent(
@@ -244,11 +243,11 @@ public final class Workspace extends Home {
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"removing app icon from workspace")) {
dragIconToWorkspace(
mLauncher, homeAppIcon,
mLauncher,
homeAppIcon,
() -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
homeAppIcon.getLongPressIndicator(),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
null);
/* expectDropEvents= */ null);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dragged the app to the drop bar")) {
@@ -274,11 +273,11 @@ public final class Workspace extends Home {
try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
"uninstalling app icon")) {
dragIconToWorkspace(
launcher, homeAppIcon,
launcher,
homeAppIcon,
() -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
homeAppIcon.getLongPressIndicator(),
expectLongClickEvents,
null);
/* expectDropEvents= */null);
launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
@@ -345,15 +344,15 @@ public final class Workspace extends Home {
}
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
Point dest, boolean startsActivity, boolean isWidgetShortcut,
Runnable expectLongClickEvents) {
Runnable expectDropEvents = null;
if (startsActivity || isWidgetShortcut) {
expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
LauncherInstrumentation.EVENT_START);
}
dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator,
expectLongClickEvents, expectDropEvents);
dragIconToWorkspace(
launcher, launchable, () -> dest, expectLongClickEvents, expectDropEvents);
}
/**
@@ -361,22 +360,27 @@ public final class Workspace extends Home {
* (There is no slow down time before drop event)
* This function expects the launchable is inside the workspace and there is no drop event.
*/
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
Supplier<Point> destSupplier, String longPressIndicator) {
dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator,
() -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
static void dragIconToWorkspace(
LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> destSupplier) {
dragIconToWorkspace(
launcher,
launchable,
destSupplier,
() -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
/* expectDropEvents= */ null);
}
static void dragIconToWorkspace(
LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
String longPressIndicator, Runnable expectLongClickEvents,
LauncherInstrumentation launcher,
Launchable launchable,
Supplier<Point> dest,
Runnable expectLongClickEvents,
@Nullable Runnable expectDropEvents) {
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
"want to drag icon to workspace")) {
final long downTime = SystemClock.uptimeMillis();
Point dragStart = launchable.startDrag(
downTime,
longPressIndicator,
expectLongClickEvents,
/* runToSpringLoadedState= */ true);
Point targetDest = dest.get();
@@ -41,7 +41,6 @@ interface WorkspaceDragSource {
? launchableCenter.x - width / 2
: launchableCenter.x + width / 2,
displaySize.y / 2),
launchable.getLongPressIndicator(),
startsActivity,
isWidgetShortcut,
launchable::addExpectedEventsForLongClick);