Snap for 5698743 from 5cd4333b5c to qt-release
Change-Id: I68733418d6f3b897113a1d41c85f044c970bb2ce
This commit is contained in:
+1
-1
@@ -298,7 +298,7 @@ LOCAL_PROGUARD_ENABLED := full
|
||||
LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
LOCAL_PRODUCT_MODULE := true
|
||||
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep Launcher3QuickStepGo
|
||||
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep
|
||||
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
|
||||
|
||||
LOCAL_FULL_LIBS_MANIFEST_FILES := \
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherStateManager.StateHandler;
|
||||
@@ -43,8 +38,6 @@ import java.util.ArrayList;
|
||||
public abstract class RecentsUiFactory {
|
||||
|
||||
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
|
||||
// Scale recents takes before animating in
|
||||
private static final float RECENTS_PREPARE_SCALE = 1.33f;
|
||||
|
||||
public static TouchController[] createTouchControllers(Launcher launcher) {
|
||||
ArrayList<TouchController> list = new ArrayList<>();
|
||||
@@ -76,18 +69,6 @@ public abstract class RecentsUiFactory {
|
||||
return new RecentsViewStateController(launcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the recents view to animate in.
|
||||
*
|
||||
* @param launcher the launcher activity
|
||||
*/
|
||||
public static void prepareToShowOverview(Launcher launcher) {
|
||||
View overview = launcher.getOverviewPanel();
|
||||
if (overview.getVisibility() != VISIBLE) {
|
||||
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean-up logic that occurs when recents is no longer in use/visible.
|
||||
*
|
||||
@@ -108,4 +89,6 @@ public abstract class RecentsUiFactory {
|
||||
public static RotationMode getRotationMode(DeviceProfile dp) {
|
||||
return RotationMode.NORMAL;
|
||||
}
|
||||
|
||||
public static void clearSwipeSharedState(boolean finishAnimation) {}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,31 @@
|
||||
|
||||
package com.android.launcher3.uioverrides.states;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
|
||||
/**
|
||||
@@ -32,6 +48,9 @@ import com.android.quickstep.views.IconRecentsView;
|
||||
*/
|
||||
public class OverviewState extends LauncherState {
|
||||
|
||||
// Scale recents takes before animating in
|
||||
private static final float RECENTS_PREPARE_SCALE = 1.33f;
|
||||
|
||||
private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
|
||||
| FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
|
||||
|
||||
@@ -103,6 +122,27 @@ public class OverviewState extends LauncherState {
|
||||
return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
|
||||
AnimatorSetBuilder builder) {
|
||||
if (fromState == NORMAL && this == OVERVIEW) {
|
||||
if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
|
||||
} else {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
|
||||
}
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
|
||||
|
||||
View overview = launcher.getOverviewPanel();
|
||||
if (overview.getVisibility() != VISIBLE) {
|
||||
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static OverviewState newBackgroundState(int id) {
|
||||
return new OverviewState(id);
|
||||
|
||||
@@ -19,15 +19,21 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYS
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Region;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.compat.UserManagerCompat;
|
||||
import com.android.systemui.shared.recents.IOverviewProxy;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
|
||||
@@ -37,7 +43,16 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class TouchInteractionService extends Service {
|
||||
|
||||
private static final String TAG = "TouchInteractionService";
|
||||
private static final String TAG = "GoTouchInteractionService";
|
||||
private boolean mIsUserUnlocked;
|
||||
private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
|
||||
initWhenUserUnlocked();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
|
||||
|
||||
@@ -53,17 +68,21 @@ public class TouchInteractionService extends Service {
|
||||
|
||||
@Override
|
||||
public void onOverviewToggle() {
|
||||
mOverviewCommandHelper.onOverviewToggle();
|
||||
if (mIsUserUnlocked) {
|
||||
mOverviewCommandHelper.onOverviewToggle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverviewShown(boolean triggeredFromAltTab) {
|
||||
mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
|
||||
if (mIsUserUnlocked) {
|
||||
mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
|
||||
if (triggeredFromAltTab && !triggeredFromHomeKey) {
|
||||
if (mIsUserUnlocked && triggeredFromAltTab && !triggeredFromHomeKey) {
|
||||
// onOverviewShownFromAltTab hides the overview and ends at the target app
|
||||
mOverviewCommandHelper.onOverviewHidden();
|
||||
}
|
||||
@@ -71,7 +90,9 @@ public class TouchInteractionService extends Service {
|
||||
|
||||
@Override
|
||||
public void onTip(int actionType, int viewType) {
|
||||
mOverviewCommandHelper.onTip(actionType, viewType);
|
||||
if (mIsUserUnlocked) {
|
||||
mOverviewCommandHelper.onTip(actionType, viewType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,17 +144,31 @@ public class TouchInteractionService extends Service {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mRecentsModel = RecentsModel.INSTANCE.get(this);
|
||||
mOverviewComponentObserver = new OverviewComponentObserver(this);
|
||||
mOverviewCommandHelper = new OverviewCommandHelper(this,
|
||||
mOverviewComponentObserver);
|
||||
if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
|
||||
initWhenUserUnlocked();
|
||||
} else {
|
||||
mIsUserUnlocked = false;
|
||||
registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
|
||||
}
|
||||
|
||||
sConnected = true;
|
||||
}
|
||||
|
||||
private void initWhenUserUnlocked() {
|
||||
mRecentsModel = RecentsModel.INSTANCE.get(this);
|
||||
mOverviewComponentObserver = new OverviewComponentObserver(this);
|
||||
mOverviewCommandHelper = new OverviewCommandHelper(this,
|
||||
mOverviewComponentObserver);
|
||||
mIsUserUnlocked = true;
|
||||
Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mOverviewComponentObserver.onDestroy();
|
||||
if (mIsUserUnlocked) {
|
||||
mOverviewComponentObserver.onDestroy();
|
||||
}
|
||||
Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
|
||||
sConnected = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath GRADLE_CLASS_PATH
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
@@ -44,12 +34,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.core:core:${ANDROID_X_VERSION}"
|
||||
}
|
||||
|
||||
@@ -85,6 +85,14 @@ public class DotRenderer {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public float[] getLeftDotPosition() {
|
||||
return mLeftDotPosition;
|
||||
}
|
||||
|
||||
public float[] getRightDotPosition() {
|
||||
return mRightDotPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a circle on top of the canvas according to the given params.
|
||||
*/
|
||||
|
||||
+32
-3
@@ -16,25 +16,32 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
|
||||
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
||||
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
|
||||
|
||||
import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
|
||||
import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
@@ -46,11 +53,12 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
*/
|
||||
public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
|
||||
|
||||
private RecentsView mRecentsView;
|
||||
public static final int INDEX_SHELF_ANIM = 0;
|
||||
public static final int INDEX_RECENTS_FADE_ANIM = 1;
|
||||
public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
|
||||
|
||||
public LauncherAppTransitionManagerImpl(Context context) {
|
||||
super(context);
|
||||
mRecentsView = mLauncher.getOverviewPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,4 +141,25 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
mLauncher.getStateManager().reapplyState();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStateElementAnimationsCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animator createStateElementAnimation(int index, float... values) {
|
||||
switch (index) {
|
||||
case INDEX_SHELF_ANIM:
|
||||
return mLauncher.getAllAppsController().createSpringAnimation(values);
|
||||
case INDEX_RECENTS_FADE_ANIM:
|
||||
return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
|
||||
RecentsView.CONTENT_ALPHA, values);
|
||||
case INDEX_RECENTS_TRANSLATE_X_ANIM:
|
||||
return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(),
|
||||
VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values);
|
||||
default:
|
||||
return super.createStateElementAnimation(index, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-17
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
|
||||
@@ -48,6 +46,7 @@ import com.android.launcher3.util.UiThreadHelper;
|
||||
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.TouchInteractionService;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
|
||||
@@ -62,9 +61,6 @@ public abstract class RecentsUiFactory {
|
||||
private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
|
||||
WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
|
||||
|
||||
// Scale recents takes before animating in
|
||||
private static final float RECENTS_PREPARE_SCALE = 1.33f;
|
||||
|
||||
public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
|
||||
@Override
|
||||
public void mapRect(int left, int top, int right, int bottom, Rect out) {
|
||||
@@ -189,19 +185,10 @@ public abstract class RecentsUiFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the recents view to animate in.
|
||||
*
|
||||
* @param launcher the launcher activity
|
||||
* Clears the swipe shared state for the current swipe gesture.
|
||||
*/
|
||||
public static void prepareToShowOverview(Launcher launcher) {
|
||||
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) {
|
||||
// Overview lives on the side, so doesn't scale in from above.
|
||||
return;
|
||||
}
|
||||
RecentsView overview = launcher.getOverviewPanel();
|
||||
if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
|
||||
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
|
||||
}
|
||||
public static void clearSwipeSharedState(boolean finishAnimation) {
|
||||
TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
@@ -83,4 +83,16 @@ public class BackgroundAppState extends OverviewState {
|
||||
public int getVisibleElements(Launcher launcher) {
|
||||
return super.getVisibleElements(launcher) & ~RECENTS_CLEAR_ALL_BUTTON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
|
||||
if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
|
||||
// Translate hotseat offscreen if we show it in overview.
|
||||
ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
|
||||
scaleAndTranslation.translationY = LayoutUtils.getShelfTrackingDistance(launcher,
|
||||
launcher.getDeviceProfile());
|
||||
return scaleAndTranslation;
|
||||
}
|
||||
return super.getHotseatScaleAndTranslation(launcher);
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -14,8 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.uioverrides.states;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
|
||||
public class OverviewPeekState extends OverviewState {
|
||||
public OverviewPeekState(int id) {
|
||||
@@ -29,4 +36,13 @@ public class OverviewPeekState extends OverviewState {
|
||||
- launcher.getResources().getDimension(R.dimen.overview_peek_distance);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
|
||||
AnimatorSetBuilder builder) {
|
||||
if (this == OVERVIEW_PEEK && fromState == NORMAL) {
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -15,8 +15,20 @@
|
||||
*/
|
||||
package com.android.launcher3.uioverrides.states;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
|
||||
|
||||
@@ -30,8 +42,11 @@ import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.uioverrides.UiFactory;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
|
||||
@@ -40,6 +55,9 @@ import com.android.quickstep.views.TaskView;
|
||||
*/
|
||||
public class OverviewState extends LauncherState {
|
||||
|
||||
// Scale recents takes before animating in
|
||||
private static final float RECENTS_PREPARE_SCALE = 1.33f;
|
||||
|
||||
protected static final Rect sTempRect = new Rect();
|
||||
|
||||
private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
|
||||
@@ -127,6 +145,10 @@ public class OverviewState extends LauncherState {
|
||||
// We have no all apps content, so we're still at the fully down progress.
|
||||
return super.getVerticalProgress(launcher);
|
||||
}
|
||||
return getDefaultVerticalProgress(launcher);
|
||||
}
|
||||
|
||||
public static float getDefaultVerticalProgress(Launcher launcher) {
|
||||
return 1 - (getDefaultSwipeHeight(launcher)
|
||||
/ launcher.getAllAppsController().getShiftRange());
|
||||
}
|
||||
@@ -156,6 +178,29 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
|
||||
AnimatorSetBuilder builder) {
|
||||
if (fromState == NORMAL && this == OVERVIEW) {
|
||||
if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
|
||||
} else {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
|
||||
|
||||
// Scale up the recents, if it is not coming from the side
|
||||
RecentsView overview = launcher.getOverviewPanel();
|
||||
if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
|
||||
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
|
||||
}
|
||||
}
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
|
||||
}
|
||||
}
|
||||
|
||||
public static OverviewState newBackgroundState(int id) {
|
||||
return new BackgroundAppState(id);
|
||||
}
|
||||
|
||||
+33
-6
@@ -16,11 +16,20 @@
|
||||
|
||||
package com.android.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
|
||||
|
||||
@@ -38,14 +47,14 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.quickstep.OverviewInteractionState;
|
||||
import com.android.quickstep.util.MotionPauseDetector;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
|
||||
/**
|
||||
* Touch controller which handles swipe and hold to go to Overview
|
||||
*/
|
||||
public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
|
||||
private static final long PEEK_ANIM_DURATION = 100;
|
||||
private static final long PEEK_IN_ANIM_DURATION = 240;
|
||||
private static final long PEEK_OUT_ANIM_DURATION = 100;
|
||||
private static final float MAX_DISPLACEMENT_PERCENT = 0.75f;
|
||||
|
||||
private final MotionPauseDetector mMotionPauseDetector;
|
||||
@@ -81,9 +90,9 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
}
|
||||
LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
|
||||
LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
|
||||
long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
|
||||
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
|
||||
new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT,
|
||||
PEEK_ANIM_DURATION);
|
||||
new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration);
|
||||
mPeekAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
@@ -106,6 +115,21 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
|
||||
LauncherState toState) {
|
||||
if (fromState == NORMAL && toState == ALL_APPS) {
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
|
||||
// Get workspace out of the way quickly, to prepare for potential pause.
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
|
||||
return builder;
|
||||
}
|
||||
return super.getAnimatorSetBuilderForStates(fromState, toState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDrag(float displacement, MotionEvent event) {
|
||||
float upDisplacement = -displacement;
|
||||
@@ -123,8 +147,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
}
|
||||
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
builder.setInterpolator(AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
|
||||
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
|
||||
builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
|
||||
}
|
||||
AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
|
||||
NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
|
||||
overviewAnim.addListener(new AnimatorListenerAdapter() {
|
||||
|
||||
+38
-61
@@ -18,12 +18,13 @@ package com.android.quickstep;
|
||||
import static android.view.View.TRANSLATION_Y;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_FADE_ANIM;
|
||||
import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_TRANSLATE_X_ANIM;
|
||||
import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
@@ -31,7 +32,6 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
@@ -48,8 +48,6 @@ import android.view.animation.Interpolator;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -60,13 +58,14 @@ import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
import com.android.quickstep.util.StaggeredWorkspaceAnim;
|
||||
import com.android.quickstep.views.LauncherRecentsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -216,10 +215,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
activity.getAppsView().getContentView().setVisibility(View.GONE);
|
||||
|
||||
return new AnimationFactory() {
|
||||
private Animator mShelfAnim;
|
||||
private ShelfAnimState mShelfState;
|
||||
private Animator mAttachToWindowFadeAnim;
|
||||
private SpringAnimation mAttachToWindowTranslationXAnim;
|
||||
private boolean mIsAttachedToWindow;
|
||||
|
||||
@Override
|
||||
@@ -252,31 +248,26 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
return;
|
||||
}
|
||||
mShelfState = shelfState;
|
||||
if (mShelfAnim != null) {
|
||||
mShelfAnim.cancel();
|
||||
}
|
||||
activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
|
||||
if (mShelfState == ShelfAnimState.CANCEL) {
|
||||
return;
|
||||
}
|
||||
float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity);
|
||||
float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity);
|
||||
// Peek based on default overview progress so we can see hotseat if we're showing
|
||||
// that instead of predictions in overview.
|
||||
float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(activity);
|
||||
float shelfPeekingProgress = shelfHiddenProgress
|
||||
- (shelfHiddenProgress - shelfOverviewProgress) * 0.25f;
|
||||
- (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
|
||||
float toProgress = mShelfState == ShelfAnimState.HIDE
|
||||
? shelfHiddenProgress
|
||||
: mShelfState == ShelfAnimState.PEEK
|
||||
? shelfPeekingProgress
|
||||
: shelfOverviewProgress;
|
||||
mShelfAnim = createShelfAnim(activity, toProgress);
|
||||
mShelfAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mShelfAnim = null;
|
||||
}
|
||||
});
|
||||
mShelfAnim.setInterpolator(interpolator);
|
||||
mShelfAnim.setDuration(duration);
|
||||
mShelfAnim.start();
|
||||
Animator shelfAnim = activity.getStateManager()
|
||||
.createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
|
||||
shelfAnim.setInterpolator(interpolator);
|
||||
shelfAnim.setDuration(duration).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -285,12 +276,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
return;
|
||||
}
|
||||
mIsAttachedToWindow = attached;
|
||||
if (mAttachToWindowFadeAnim != null) {
|
||||
mAttachToWindowFadeAnim.cancel();
|
||||
}
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
mAttachToWindowFadeAnim = ObjectAnimator.ofFloat(recentsView,
|
||||
RecentsView.CONTENT_ALPHA, attached ? 1 : 0);
|
||||
LauncherRecentsView recentsView = activity.getOverviewPanel();
|
||||
Animator fadeAnim = activity.getStateManager()
|
||||
.createStateElementAnimation(
|
||||
INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
|
||||
|
||||
int runningTaskIndex = recentsView.getRunningTaskIndex();
|
||||
if (runningTaskIndex == 0) {
|
||||
@@ -312,33 +301,28 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
|
||||
float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0;
|
||||
float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX;
|
||||
if (mAttachToWindowTranslationXAnim == null) {
|
||||
mAttachToWindowTranslationXAnim = new SpringAnimation(recentsView,
|
||||
SpringAnimation.TRANSLATION_X).setSpring(new SpringForce()
|
||||
.setDampingRatio(0.8f)
|
||||
.setStiffness(250));
|
||||
}
|
||||
activity.getStateManager()
|
||||
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
|
||||
|
||||
if (!recentsView.isShown() && animate) {
|
||||
recentsView.setTranslationX(fromTranslationX);
|
||||
mAttachToWindowTranslationXAnim.setStartValue(fromTranslationX);
|
||||
}
|
||||
mAttachToWindowTranslationXAnim.animateToFinalPosition(toTranslationX);
|
||||
if (!animate && mAttachToWindowTranslationXAnim.canSkipToEnd()) {
|
||||
mAttachToWindowTranslationXAnim.skipToEnd();
|
||||
} else {
|
||||
fromTranslationX = recentsView.getTranslationX();
|
||||
}
|
||||
|
||||
mAttachToWindowFadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
|
||||
} else {
|
||||
mAttachToWindowFadeAnim.setInterpolator(ACCEL_DEACCEL);
|
||||
}
|
||||
mAttachToWindowFadeAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mAttachToWindowFadeAnim = null;
|
||||
if (!animate) {
|
||||
recentsView.setTranslationX(toTranslationX);
|
||||
} else {
|
||||
activity.getStateManager().createStateElementAnimation(
|
||||
INDEX_RECENTS_TRANSLATE_X_ANIM,
|
||||
fromTranslationX, toTranslationX).start();
|
||||
}
|
||||
});
|
||||
mAttachToWindowFadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0);
|
||||
mAttachToWindowFadeAnim.start();
|
||||
|
||||
fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
|
||||
} else {
|
||||
fadeAnim.setInterpolator(ACCEL_DEACCEL);
|
||||
}
|
||||
fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -354,10 +338,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
if (!activity.getDeviceProfile().isVerticalBarLayout()
|
||||
&& SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
|
||||
// Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically.
|
||||
Animator shiftAnim = createShelfAnim(activity,
|
||||
anim.play(activity.getStateManager().createStateElementAnimation(
|
||||
INDEX_SHELF_ANIM,
|
||||
fromState.getVerticalProgress(activity),
|
||||
endState.getVerticalProgress(activity));
|
||||
anim.play(shiftAnim);
|
||||
endState.getVerticalProgress(activity)));
|
||||
}
|
||||
playScaleDownAnim(anim, activity, fromState, endState);
|
||||
|
||||
@@ -375,13 +359,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
callback.accept(controller);
|
||||
}
|
||||
|
||||
private Animator createShelfAnim(Launcher activity, float ... progressValues) {
|
||||
Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
|
||||
"allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(),
|
||||
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
|
||||
return shiftAnim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale down recents from the center task being full screen to being in overview.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,8 @@ import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import static com.android.launcher3.Utilities.FLAG_NO_GESTURES;
|
||||
|
||||
import android.view.InputEvent;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
@@ -182,7 +184,10 @@ public class RecentsAnimationWrapper {
|
||||
}
|
||||
}
|
||||
if (mInputConsumer != null) {
|
||||
int flags = ev.getEdgeFlags();
|
||||
ev.setEdgeFlags(flags | FLAG_NO_GESTURES);
|
||||
mInputConsumer.onMotionEvent(ev);
|
||||
ev.setEdgeFlags(flags);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -72,13 +72,15 @@ public class SwipeSharedState implements SwipeAnimationListener {
|
||||
mLastAnimationRunning = false;
|
||||
}
|
||||
|
||||
private void clearListenerState() {
|
||||
private void clearListenerState(boolean finishAnimation) {
|
||||
if (mRecentsAnimationListener != null) {
|
||||
mRecentsAnimationListener.removeListener(this);
|
||||
mRecentsAnimationListener.cancelListener();
|
||||
if (mLastAnimationRunning && mLastAnimationTarget != null) {
|
||||
Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(),
|
||||
mLastAnimationTarget::cancelAnimation);
|
||||
finishAnimation
|
||||
? mLastAnimationTarget::finishAnimation
|
||||
: mLastAnimationTarget::cancelAnimation);
|
||||
mLastAnimationTarget = null;
|
||||
}
|
||||
}
|
||||
@@ -106,7 +108,7 @@ public class SwipeSharedState implements SwipeAnimationListener {
|
||||
}
|
||||
}
|
||||
|
||||
clearListenerState();
|
||||
clearListenerState(false /* finishAnimation */);
|
||||
boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
|
||||
: mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
|
||||
mRecentsAnimationListener = new RecentsAnimationListenerSet(
|
||||
@@ -138,8 +140,8 @@ public class SwipeSharedState implements SwipeAnimationListener {
|
||||
mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
|
||||
}
|
||||
|
||||
public void clearAllState() {
|
||||
clearListenerState();
|
||||
public void clearAllState(boolean finishAnimation) {
|
||||
clearListenerState(finishAnimation);
|
||||
canGestureBeContinued = false;
|
||||
recentsAnimationFinishInterrupted = false;
|
||||
nextRunningTaskId = -1;
|
||||
|
||||
@@ -29,8 +29,6 @@ import android.view.View;
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
@@ -123,6 +121,7 @@ public final class TaskViewUtils {
|
||||
new RemoteAnimationTargetSet(targets, MODE_OPENING);
|
||||
targetSet.addDependentTransactionApplier(applier);
|
||||
|
||||
final RecentsView recentsView = v.getRecentsView();
|
||||
final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
|
||||
@@ -153,7 +152,10 @@ public final class TaskViewUtils {
|
||||
// TODO: Take into account the current fullscreen progress for animating the insets
|
||||
params.setProgress(1 - percent);
|
||||
RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
|
||||
if (!skipViewChanges) {
|
||||
int taskIndex = recentsView.indexOfChild(v);
|
||||
int centerTaskIndex = recentsView.getCurrentPage();
|
||||
boolean parallaxCenterAndAdjacentTask = taskIndex != centerTaskIndex;
|
||||
if (!skipViewChanges && parallaxCenterAndAdjacentTask) {
|
||||
float scale = taskBounds.width() / mThumbnailRect.width();
|
||||
v.setScaleX(scale);
|
||||
v.setScaleY(scale);
|
||||
|
||||
+61
-19
@@ -81,6 +81,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
|
||||
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.AssistantTouchConsumer;
|
||||
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.InputConsumer;
|
||||
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
|
||||
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
|
||||
@@ -224,14 +225,18 @@ public class TouchInteractionService extends Service implements
|
||||
};
|
||||
|
||||
private static boolean sConnected = false;
|
||||
private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState();
|
||||
|
||||
public static boolean isConnected() {
|
||||
return sConnected;
|
||||
}
|
||||
|
||||
private final SwipeSharedState mSwipeSharedState = new SwipeSharedState();
|
||||
public static SwipeSharedState getSwipeSharedState() {
|
||||
return sSwipeSharedState;
|
||||
}
|
||||
|
||||
private final InputConsumer mResetGestureInputConsumer =
|
||||
new ResetGestureInputConsumer(mSwipeSharedState);
|
||||
new ResetGestureInputConsumer(sSwipeSharedState);
|
||||
|
||||
private ActivityManagerWrapper mAM;
|
||||
private RecentsModel mRecentsModel;
|
||||
@@ -267,6 +272,9 @@ public class TouchInteractionService extends Service implements
|
||||
private Mode mMode = Mode.THREE_BUTTONS;
|
||||
private int mDefaultDisplayId;
|
||||
private final RectF mSwipeTouchRegion = new RectF();
|
||||
private final RectF mAssistantLeftRegion = new RectF();
|
||||
private final RectF mAssistantRightRegion = new RectF();
|
||||
|
||||
private ComponentName mGestureBlockingActivity;
|
||||
|
||||
private Region mExclusionRegion;
|
||||
@@ -349,9 +357,25 @@ public class TouchInteractionService extends Service implements
|
||||
defaultDisplay.getRealSize(realSize);
|
||||
mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y);
|
||||
if (mMode == Mode.NO_BUTTON) {
|
||||
mSwipeTouchRegion.top = mSwipeTouchRegion.bottom -
|
||||
getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
|
||||
int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
|
||||
mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - touchHeight;
|
||||
|
||||
final int assistantWidth = getResources()
|
||||
.getDimensionPixelSize(R.dimen.gestures_assistant_width);
|
||||
final float assistantHeight = Math.max(touchHeight,
|
||||
QuickStepContract.getWindowCornerRadius(getResources()));
|
||||
mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeTouchRegion.bottom;
|
||||
mAssistantLeftRegion.top = mAssistantRightRegion.top =
|
||||
mSwipeTouchRegion.bottom - assistantHeight;
|
||||
|
||||
mAssistantLeftRegion.left = 0;
|
||||
mAssistantLeftRegion.right = assistantWidth;
|
||||
|
||||
mAssistantRightRegion.right = mSwipeTouchRegion.right;
|
||||
mAssistantRightRegion.left = mSwipeTouchRegion.right - assistantWidth;
|
||||
} else {
|
||||
mAssistantLeftRegion.setEmpty();
|
||||
mAssistantRightRegion.setEmpty();
|
||||
switch (defaultDisplay.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
mSwipeTouchRegion.left = mSwipeTouchRegion.right
|
||||
@@ -416,7 +440,7 @@ public class TouchInteractionService extends Service implements
|
||||
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
|
||||
mIsUserUnlocked = true;
|
||||
|
||||
mSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
|
||||
sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
|
||||
mInputConsumer.registerInputConsumer();
|
||||
onSystemUiProxySet();
|
||||
onSystemUiFlagsChanged();
|
||||
@@ -491,6 +515,15 @@ public class TouchInteractionService extends Service implements
|
||||
mConsumer = newConsumer(useSharedState, event);
|
||||
TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
|
||||
mUncheckedConsumer = mConsumer;
|
||||
} else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON
|
||||
&& canTriggerAssistantAction(event)) {
|
||||
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
|
||||
// not interrupt it. QuickSwitch assumes that interruption can only happen if the
|
||||
// next gesture is also quick switch.
|
||||
mUncheckedConsumer =
|
||||
new AssistantTouchConsumer(this, mISystemUiProxy,
|
||||
mOverviewComponentObserver.getActivityControlHelper(),
|
||||
InputConsumer.NO_OP, mInputMonitorCompat);
|
||||
} else {
|
||||
mUncheckedConsumer = InputConsumer.NO_OP;
|
||||
}
|
||||
@@ -505,6 +538,14 @@ public class TouchInteractionService extends Service implements
|
||||
|| (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
|
||||
}
|
||||
|
||||
private boolean canTriggerAssistantAction(MotionEvent ev) {
|
||||
return mAssistantAvailable
|
||||
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
|
||||
&& (mAssistantLeftRegion.contains(ev.getX(), ev.getY()) ||
|
||||
mAssistantRightRegion.contains(ev.getX(), ev.getY()))
|
||||
&& !ActivityManagerWrapper.getInstance().isLockToAppActive();
|
||||
}
|
||||
|
||||
private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
|
||||
boolean isInValidSystemUiState = validSystemUiFlags();
|
||||
|
||||
@@ -525,10 +566,7 @@ public class TouchInteractionService extends Service implements
|
||||
if (mMode == Mode.NO_BUTTON) {
|
||||
final ActivityControlHelper activityControl =
|
||||
mOverviewComponentObserver.getActivityControlHelper();
|
||||
if (mAssistantAvailable
|
||||
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
|
||||
&& AssistantTouchConsumer.withinTouchRegion(this, event)
|
||||
&& !ActivityManagerWrapper.getInstance().isLockToAppActive()) {
|
||||
if (canTriggerAssistantAction(event)) {
|
||||
base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
|
||||
mInputMonitorCompat);
|
||||
}
|
||||
@@ -555,7 +593,7 @@ public class TouchInteractionService extends Service implements
|
||||
private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
|
||||
final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
|
||||
if (!useSharedState) {
|
||||
mSwipeSharedState.clearAllState();
|
||||
sSwipeSharedState.clearAllState(false /* finishAnimation */);
|
||||
}
|
||||
if ((mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0) {
|
||||
// This handles apps showing over the lockscreen (e.g. camera)
|
||||
@@ -565,22 +603,26 @@ public class TouchInteractionService extends Service implements
|
||||
final ActivityControlHelper activityControl =
|
||||
mOverviewComponentObserver.getActivityControlHelper();
|
||||
|
||||
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher
|
||||
&& !mSwipeSharedState.recentsAnimationFinishInterrupted) {
|
||||
if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher
|
||||
&& !sSwipeSharedState.recentsAnimationFinishInterrupted) {
|
||||
return mResetGestureInputConsumer;
|
||||
} else if (mSwipeSharedState.recentsAnimationFinishInterrupted) {
|
||||
} else if (sSwipeSharedState.recentsAnimationFinishInterrupted) {
|
||||
// If the finish animation was interrupted, then continue using the other activity input
|
||||
// consumer but with the next task as the running task
|
||||
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||
info.id = mSwipeSharedState.nextRunningTaskId;
|
||||
info.id = sSwipeSharedState.nextRunningTaskId;
|
||||
return createOtherActivityInputConsumer(event, info);
|
||||
} else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
|
||||
} else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
|
||||
return createOverviewInputConsumer(event);
|
||||
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) {
|
||||
return createOverviewInputConsumer(event);
|
||||
} else if (mGestureBlockingActivity != null && runningTaskInfo != null
|
||||
&& mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
|
||||
return mResetGestureInputConsumer;
|
||||
} else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
|
||||
return new FallbackNoButtonInputConsumer(this, activityControl,
|
||||
mInputMonitorCompat, sSwipeSharedState, mSwipeTouchRegion,
|
||||
mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo);
|
||||
} else {
|
||||
return createOtherActivityInputConsumer(event, runningTaskInfo);
|
||||
}
|
||||
@@ -602,13 +644,13 @@ public class TouchInteractionService extends Service implements
|
||||
return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
|
||||
mOverviewComponentObserver.getOverviewIntent(), activityControl,
|
||||
shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
|
||||
mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
|
||||
sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
|
||||
disableHorizontalSwipe(event));
|
||||
}
|
||||
|
||||
private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
|
||||
if (mMode == Mode.NO_BUTTON && taskInfo != null) {
|
||||
return new DeviceLockedInputConsumer(this, mSwipeSharedState, mInputMonitorCompat,
|
||||
return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat,
|
||||
mSwipeTouchRegion, taskInfo.taskId);
|
||||
} else {
|
||||
return mResetGestureInputConsumer;
|
||||
@@ -623,7 +665,7 @@ public class TouchInteractionService extends Service implements
|
||||
return mResetGestureInputConsumer;
|
||||
}
|
||||
|
||||
if (activity.getRootView().hasWindowFocus() || mSwipeSharedState.goingToLauncher) {
|
||||
if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) {
|
||||
return new OverviewInputConsumer(activity, mInputMonitorCompat,
|
||||
false /* startingInActivityBounds */);
|
||||
} else {
|
||||
@@ -670,7 +712,7 @@ public class TouchInteractionService extends Service implements
|
||||
+ mOverviewComponentObserver.getActivityControlHelper().isResumed());
|
||||
pw.println(" useSharedState=" + mConsumer.useSharedSwipeState());
|
||||
if (mConsumer.useSharedSwipeState()) {
|
||||
mSwipeSharedState.dump(" ", pw);
|
||||
sSwipeSharedState.dump(" ", pw);
|
||||
}
|
||||
pw.println(" mConsumer=" + mConsumer.getName());
|
||||
pw.println("FeatureFlags:");
|
||||
|
||||
+40
-46
@@ -39,6 +39,8 @@ import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
@@ -47,17 +49,14 @@ import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.quickstep.ActivityControlHelper;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
|
||||
/**
|
||||
* Touch consumer for handling events to launch assistant from launcher
|
||||
*/
|
||||
public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
implements SwipeDetector.Listener {
|
||||
public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
|
||||
private static final String TAG = "AssistantTouchConsumer";
|
||||
private static final long RETRACT_ANIMATION_DURATION_MS = 300;
|
||||
@@ -68,7 +67,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
|
||||
private static final String INVOCATION_TYPE_KEY = "invocation_type";
|
||||
private static final int INVOCATION_TYPE_GESTURE = 1;
|
||||
private static final int INVOCATION_TYPE_FLING = 6;
|
||||
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
@@ -90,7 +88,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
private final float mSquaredSlop;
|
||||
private final ISystemUiProxy mSysUiProxy;
|
||||
private final Context mContext;
|
||||
private final SwipeDetector mSwipeDetector;
|
||||
private final GestureDetector mGestureDetector;
|
||||
|
||||
public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
|
||||
ActivityControlHelper activityControlHelper, InputConsumer delegate,
|
||||
@@ -107,8 +105,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
|
||||
mSquaredSlop = slop * slop;
|
||||
mActivityControlHelper = activityControlHelper;
|
||||
mSwipeDetector = new SwipeDetector(mContext, this, SwipeDetector.VERTICAL);
|
||||
mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
|
||||
|
||||
mGestureDetector = new GestureDetector(context, new AssistantGestureListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,7 +117,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
// TODO add logging
|
||||
mSwipeDetector.onTouchEvent(ev);
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case ACTION_DOWN: {
|
||||
@@ -171,13 +169,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
mStartDragPos.set(mLastPos.x, mLastPos.y);
|
||||
mDragTime = SystemClock.uptimeMillis();
|
||||
|
||||
// Determine if angle is larger than threshold for assistant detection
|
||||
float angle = (float) Math.toDegrees(
|
||||
Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x));
|
||||
mDirection = angle > 90 ? UPLEFT : UPRIGHT;
|
||||
angle = angle > 90 ? 180 - angle : angle;
|
||||
|
||||
if (angle > mAngleThreshold && angle < 90) {
|
||||
if (isValidAssistantGestureAngle(
|
||||
mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) {
|
||||
setActive(ev);
|
||||
} else {
|
||||
mState = STATE_DELEGATE_ACTIVE;
|
||||
@@ -261,40 +254,41 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean withinTouchRegion(Context context, MotionEvent ev) {
|
||||
final Resources res = context.getResources();
|
||||
final int width = res.getDisplayMetrics().widthPixels;
|
||||
final int height = res.getDisplayMetrics().heightPixels;
|
||||
final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size);
|
||||
return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size;
|
||||
/**
|
||||
* Determine if angle is larger than threshold for assistant detection
|
||||
*/
|
||||
private boolean isValidAssistantGestureAngle(float deltaX, float deltaY) {
|
||||
float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
|
||||
mDirection = angle > 90 ? UPLEFT : UPRIGHT;
|
||||
|
||||
// normalize so that angle is measured clockwise from horizontal in the bottom right corner
|
||||
// and counterclockwise from horizontal in the bottom left corner
|
||||
angle = angle > 90 ? 180 - angle : angle;
|
||||
return (angle > mAngleThreshold && angle < 90);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragStart(boolean start) {
|
||||
// do nothing
|
||||
}
|
||||
private class AssistantGestureListener extends SimpleOnGestureListener {
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
if (isValidAssistantGestureAngle(velocityX, -velocityY)
|
||||
&& !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) {
|
||||
mLastProgress = 1;
|
||||
try {
|
||||
mSysUiProxy.onAssistantGestureCompletion(
|
||||
(float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
|
||||
startAssistantInternal(FLING);
|
||||
|
||||
@Override
|
||||
public boolean onDrag(float displacement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
if (fling && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) {
|
||||
mLastProgress = 1;
|
||||
try {
|
||||
mSysUiProxy.onAssistantGestureCompletion(velocity);
|
||||
startAssistantInternal(FLING);
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
|
||||
mSysUiProxy.startAssistant(args);
|
||||
mLaunchedAssistant = true;
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + mLastProgress,
|
||||
e);
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
|
||||
mSysUiProxy.startAssistant(args);
|
||||
mLaunchedAssistant = true;
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG,
|
||||
"Failed to send SysUI start/send assistant progress: " + mLastProgress,
|
||||
e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.quickstep.inputconsumers;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_MOVE;
|
||||
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_POINTER_UP;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
import static android.view.MotionEvent.INVALID_POINTER_ID;
|
||||
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION;
|
||||
import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.ActivityControlHelper;
|
||||
import com.android.quickstep.OverviewComponentObserver;
|
||||
import com.android.quickstep.SwipeSharedState;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
|
||||
import com.android.quickstep.util.NavBarPosition;
|
||||
import com.android.quickstep.util.RecentsAnimationListenerSet;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.BackgroundExecutor;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener {
|
||||
|
||||
private static final int STATE_NOT_FINISHED = 0;
|
||||
private static final int STATE_FINISHED_TO_HOME = 1;
|
||||
private static final int STATE_FINISHED_TO_APP = 2;
|
||||
|
||||
private static final float PROGRESS_TO_END_GESTURE = -2;
|
||||
|
||||
private final ActivityControlHelper mActivityControlHelper;
|
||||
private final InputMonitorCompat mInputMonitor;
|
||||
private final Context mContext;
|
||||
private final NavBarPosition mNavBarPosition;
|
||||
private final SwipeSharedState mSwipeSharedState;
|
||||
private final OverviewComponentObserver mOverviewComponentObserver;
|
||||
private final int mRunningTaskId;
|
||||
|
||||
private final ClipAnimationHelper mClipAnimationHelper;
|
||||
private final TransformParams mTransformParams = new TransformParams();
|
||||
private final float mTransitionDragLength;
|
||||
private final DeviceProfile mDP;
|
||||
|
||||
private final RectF mSwipeTouchRegion;
|
||||
private final boolean mDisableHorizontalSwipe;
|
||||
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
|
||||
private int mActivePointerId = -1;
|
||||
// Slop used to determine when we say that the gesture has started.
|
||||
private boolean mPassedPilferInputSlop;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
// Distance after which we start dragging the window.
|
||||
private final float mTouchSlop;
|
||||
|
||||
// Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
|
||||
private float mStartDisplacement;
|
||||
private SwipeAnimationTargetSet mSwipeAnimationTargetSet;
|
||||
private float mProgress;
|
||||
|
||||
private int mState = STATE_NOT_FINISHED;
|
||||
|
||||
public FallbackNoButtonInputConsumer(Context context,
|
||||
ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor,
|
||||
SwipeSharedState swipeSharedState, RectF swipeTouchRegion,
|
||||
OverviewComponentObserver overviewComponentObserver,
|
||||
boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) {
|
||||
mContext = context;
|
||||
mActivityControlHelper = activityControlHelper;
|
||||
mInputMonitor = inputMonitor;
|
||||
mOverviewComponentObserver = overviewComponentObserver;
|
||||
mRunningTaskId = runningTaskInfo.id;
|
||||
|
||||
mSwipeSharedState = swipeSharedState;
|
||||
mSwipeTouchRegion = swipeTouchRegion;
|
||||
mDisableHorizontalSwipe = disableHorizontalSwipe;
|
||||
|
||||
mNavBarPosition = new NavBarPosition(context);
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
|
||||
mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO
|
||||
* ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
mClipAnimationHelper = new ClipAnimationHelper(context);
|
||||
|
||||
mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context);
|
||||
Rect tempRect = new Rect();
|
||||
mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
|
||||
mDP, context, tempRect);
|
||||
mClipAnimationHelper.updateTargetRect(tempRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return TYPE_FALLBACK_NO_BUTTON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
if (mVelocityTracker == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mVelocityTracker.addMovement(ev);
|
||||
if (ev.getActionMasked() == ACTION_POINTER_UP) {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case ACTION_DOWN: {
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mDownPos.set(ev.getX(), ev.getY());
|
||||
mLastPos.set(mDownPos);
|
||||
break;
|
||||
}
|
||||
case ACTION_POINTER_DOWN: {
|
||||
if (!mPassedPilferInputSlop) {
|
||||
// Cancel interaction in case of multi-touch interaction
|
||||
int ptrIdx = ev.getActionIndex();
|
||||
if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
|
||||
forceCancelGesture(ev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_POINTER_UP: {
|
||||
int ptrIdx = ev.getActionIndex();
|
||||
int ptrId = ev.getPointerId(ptrIdx);
|
||||
if (ptrId == mActivePointerId) {
|
||||
final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
|
||||
mDownPos.set(
|
||||
ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
|
||||
ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
|
||||
mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
|
||||
mActivePointerId = ev.getPointerId(newPointerIdx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_MOVE: {
|
||||
int pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (pointerIndex == INVALID_POINTER_ID) {
|
||||
break;
|
||||
}
|
||||
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
|
||||
float displacement = getDisplacement(ev);
|
||||
|
||||
if (!mPassedPilferInputSlop) {
|
||||
if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x)
|
||||
> Math.abs(mLastPos.y - mDownPos.y)) {
|
||||
// Horizontal gesture is not allowed in this region
|
||||
forceCancelGesture(ev);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Math.abs(displacement) >= mTouchSlop) {
|
||||
mPassedPilferInputSlop = true;
|
||||
|
||||
// Deferred gesture, start the animation and gesture tracking once
|
||||
// we pass the actual touch slop
|
||||
startTouchTrackingForWindowAnimation(displacement);
|
||||
}
|
||||
} else {
|
||||
updateDisplacement(displacement - mStartDisplacement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_CANCEL:
|
||||
case ACTION_UP: {
|
||||
finishTouchTracking(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startTouchTrackingForWindowAnimation(float displacement) {
|
||||
mStartDisplacement = Math.min(displacement, -mTouchSlop);
|
||||
|
||||
RecentsAnimationListenerSet listenerSet =
|
||||
mSwipeSharedState.newRecentsAnimationListenerSet();
|
||||
listenerSet.addListener(this);
|
||||
Intent homeIntent = mOverviewComponentObserver.getHomeIntent();
|
||||
BackgroundExecutor.get().submit(
|
||||
() -> ActivityManagerWrapper.getInstance().startRecentsActivity(
|
||||
homeIntent, null, listenerSet, null, null));
|
||||
|
||||
ActivityManagerWrapper.getInstance().closeSystemWindows(
|
||||
CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
|
||||
mInputMonitor.pilferPointers();
|
||||
}
|
||||
|
||||
private void updateDisplacement(float displacement) {
|
||||
mProgress = displacement / mTransitionDragLength;
|
||||
mTransformParams.setProgress(mProgress);
|
||||
|
||||
if (mSwipeAnimationTargetSet != null) {
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceCancelGesture(MotionEvent ev) {
|
||||
int action = ev.getAction();
|
||||
ev.setAction(ACTION_CANCEL);
|
||||
finishTouchTracking(ev);
|
||||
ev.setAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the gesture has ended. Does not correlate to the completion of the interaction as
|
||||
* the animation can still be running.
|
||||
*/
|
||||
private void finishTouchTracking(MotionEvent ev) {
|
||||
if (ev.getAction() == ACTION_CANCEL) {
|
||||
mState = STATE_FINISHED_TO_APP;
|
||||
} else {
|
||||
mVelocityTracker.computeCurrentVelocity(1000,
|
||||
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
|
||||
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
|
||||
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
|
||||
float velocity = mNavBarPosition.isRightEdge() ? velocityX
|
||||
: mNavBarPosition.isLeftEdge() ? -velocityX
|
||||
: velocityY;
|
||||
float flingThreshold = mContext.getResources()
|
||||
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
|
||||
boolean isFling = Math.abs(velocity) > flingThreshold;
|
||||
|
||||
boolean goingHome;
|
||||
if (!isFling) {
|
||||
goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
} else {
|
||||
goingHome = velocity < 0;
|
||||
}
|
||||
|
||||
if (goingHome) {
|
||||
mState = STATE_FINISHED_TO_HOME;
|
||||
} else {
|
||||
mState = STATE_FINISHED_TO_APP;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSwipeAnimationTargetSet != null) {
|
||||
finishAnimationTargetSet();
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAnimationTargetSet() {
|
||||
if (mState == STATE_FINISHED_TO_APP) {
|
||||
mSwipeAnimationTargetSet.finishController(false, null, false);
|
||||
} else {
|
||||
if (mProgress < PROGRESS_TO_END_GESTURE) {
|
||||
mSwipeAnimationTargetSet.finishController(true, null, true);
|
||||
} else {
|
||||
long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1)
|
||||
* MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE));
|
||||
if (duration < 0) {
|
||||
duration = MIN_SWIPE_DURATION;
|
||||
}
|
||||
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE);
|
||||
anim.addUpdateListener(a -> {
|
||||
float p = (Float) anim.getAnimatedValue();
|
||||
mTransformParams.setProgress(p);
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
});
|
||||
anim.setDuration(duration);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mSwipeAnimationTargetSet.finishController(true, null, true);
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
|
||||
mSwipeAnimationTargetSet = targetSet;
|
||||
Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx);
|
||||
RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
|
||||
|
||||
mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class));
|
||||
if (runningTaskTarget != null) {
|
||||
mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
|
||||
}
|
||||
mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */);
|
||||
|
||||
overviewStackBounds
|
||||
.inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5);
|
||||
mClipAnimationHelper.updateTargetRect(overviewStackBounds);
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
|
||||
if (mState != STATE_NOT_FINISHED) {
|
||||
finishAnimationTargetSet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled() { }
|
||||
|
||||
private float getDisplacement(MotionEvent ev) {
|
||||
if (mNavBarPosition.isRightEdge()) {
|
||||
return ev.getX() - mDownPos.x;
|
||||
} else if (mNavBarPosition.isLeftEdge()) {
|
||||
return mDownPos.x - ev.getX();
|
||||
} else {
|
||||
return ev.getY() - mDownPos.y;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowInterceptByParent() {
|
||||
return !mPassedPilferInputSlop;
|
||||
}
|
||||
}
|
||||
+2
@@ -33,6 +33,7 @@ public interface InputConsumer {
|
||||
int TYPE_SCREEN_PINNED = 1 << 6;
|
||||
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
|
||||
int TYPE_RESET_GESTURE = 1 << 8;
|
||||
int TYPE_FALLBACK_NO_BUTTON = 1 << 9;
|
||||
|
||||
String[] NAMES = new String[] {
|
||||
"TYPE_NO_OP", // 0
|
||||
@@ -44,6 +45,7 @@ public interface InputConsumer {
|
||||
"TYPE_SCREEN_PINNED", // 6
|
||||
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
|
||||
"TYPE_RESET_GESTURE", // 8
|
||||
"TYPE_FALLBACK_NO_BUTTON", // 9
|
||||
};
|
||||
|
||||
InputConsumer NO_OP = () -> TYPE_NO_OP;
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
private static final String UP_EVT = "OtherActivityInputConsumer.UP";
|
||||
|
||||
// TODO: Move to quickstep contract
|
||||
private static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
|
||||
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
|
||||
|
||||
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
|
||||
private final RunningTaskInfo mRunningTask;
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class ResetGestureInputConsumer implements InputConsumer {
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN
|
||||
&& mSwipeSharedState.getActiveListener() != null) {
|
||||
mSwipeSharedState.clearAllState();
|
||||
mSwipeSharedState.clearAllState(false /* finishAnimation */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -17,19 +17,15 @@ package com.android.quickstep.util;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationConfig;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
@@ -40,6 +36,7 @@ import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
@@ -147,9 +144,8 @@ public class StaggeredWorkspaceAnim {
|
||||
long startDelay = (long) ((invertedRow + 1) * APP_CLOSE_ROW_START_DELAY_MS);
|
||||
|
||||
v.setTranslationY(mSpringTransY);
|
||||
SpringObjectAnimator springTransY = new SpringObjectAnimator<>(
|
||||
new ViewProgressProperty(v, View.TRANSLATION_Y), "staggeredSpringTransY", 1f,
|
||||
DAMPING_RATIO, STIFFNESS, mSpringTransY, 0);
|
||||
SpringObjectAnimator springTransY = new SpringObjectAnimator<>(v, VIEW_TRANSLATE_Y,
|
||||
1f, DAMPING_RATIO, STIFFNESS, mSpringTransY, 0);
|
||||
springTransY.setStartDelay(startDelay);
|
||||
mAnimators.add(springTransY);
|
||||
|
||||
|
||||
+4
@@ -106,6 +106,10 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
|
||||
finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
|
||||
}
|
||||
|
||||
public void finishAnimation() {
|
||||
finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
|
||||
}
|
||||
|
||||
public interface SwipeAnimationListener {
|
||||
|
||||
void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet);
|
||||
|
||||
+14
@@ -33,9 +33,11 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Hotseat;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.StateListener;
|
||||
@@ -250,4 +252,16 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
|
||||
setDisallowScrollToClearAll(!hasClearAllButton);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// Allow touches to go through to the hotseat.
|
||||
Hotseat hotseat = mActivity.getHotseat();
|
||||
boolean touchingHotseat = hotseat.isShown()
|
||||
&& mActivity.getDragLayer().isEventOverView(hotseat, ev, this);
|
||||
return !touchingHotseat;
|
||||
}
|
||||
return super.shouldStealTouchFromSiblingsBelow(ev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
|
||||
|
||||
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
|
||||
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
|
||||
import static com.android.launcher3.Utilities.squaredHypot;
|
||||
import static com.android.launcher3.Utilities.squaredTouchSlop;
|
||||
@@ -79,7 +83,6 @@ import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
@@ -124,10 +127,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
|
||||
private static final String TAG = RecentsView.class.getSimpleName();
|
||||
|
||||
public static final float SPRING_MIN_VISIBLE_CHANGE = 0.001f;
|
||||
public static final float SPRING_DAMPING_RATIO = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_MEDIUM;
|
||||
|
||||
public static final FloatProperty<RecentsView> CONTENT_ALPHA =
|
||||
new FloatProperty<RecentsView>("contentAlpha") {
|
||||
@Override
|
||||
@@ -521,6 +520,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
|
||||
|
||||
// Do not let touch escape to siblings below this view.
|
||||
return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev);
|
||||
}
|
||||
|
||||
protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1027,9 +1030,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
|
||||
addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
|
||||
if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView)
|
||||
addAnim(new SpringObjectAnimator<>(new ViewProgressProperty(taskView,
|
||||
View.TRANSLATION_Y), "taskViewTransY", SPRING_MIN_VISIBLE_CHANGE,
|
||||
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, 0, -taskView.getHeight()),
|
||||
addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y,
|
||||
MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
|
||||
SpringForce.STIFFNESS_MEDIUM,
|
||||
0, -taskView.getHeight()),
|
||||
duration, LINEAR, anim);
|
||||
else {
|
||||
addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
|
||||
@@ -1105,10 +1109,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
|
||||
if (scrollDiff != 0) {
|
||||
if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) {
|
||||
addAnim(new SpringObjectAnimator<>(
|
||||
new ViewProgressProperty(child, View.TRANSLATION_X),
|
||||
"taskViewTransX", SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO,
|
||||
SPRING_STIFFNESS, 0, scrollDiff), duration, ACCEL, anim);
|
||||
addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X,
|
||||
MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
|
||||
SpringForce.STIFFNESS_MEDIUM,
|
||||
0, scrollDiff), duration, ACCEL, anim);
|
||||
} else {
|
||||
addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration,
|
||||
ACCEL, anim);
|
||||
|
||||
+4
-1
@@ -367,8 +367,11 @@ public class TaskThumbnailView extends View {
|
||||
}
|
||||
|
||||
mRotated = isRotated;
|
||||
updateOverlay();
|
||||
invalidate();
|
||||
|
||||
// Update can be called from {@link #onSizeChanged} during layout, post handling of overlay
|
||||
// as overlay could modify the views in the overlay as a side effect of its update.
|
||||
post(this::updateOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -368,6 +368,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
} else {
|
||||
mSnapshotView.setThumbnail(null, null);
|
||||
setIcon(null);
|
||||
// Reset the task thumbnail reference as well (it will be fetched from the cache or
|
||||
// reloaded next time we need it)
|
||||
mTask.thumbnail = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,9 +491,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
mSnapshotView.setThumbnail(mTask, null);
|
||||
setOverlayEnabled(false);
|
||||
onTaskListVisibilityChanged(false);
|
||||
if (mTask != null) {
|
||||
mTask.thumbnail = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<dimen name="recents_page_spacing">10dp</dimen>
|
||||
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
|
||||
<dimen name="overview_peek_distance">32dp</dimen>
|
||||
<dimen name="overview_peek_distance">96dp</dimen>
|
||||
|
||||
<!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
|
||||
loading full resolution screenshots. -->
|
||||
@@ -66,7 +66,8 @@
|
||||
<dimen name="shelf_surface_offset">24dp</dimen>
|
||||
|
||||
<!-- Assistant Gestures -->
|
||||
<dimen name="gestures_assistant_size">48dp</dimen>
|
||||
<!-- Distance from the vertical edges of the screen in which assist gestures are recognized -->
|
||||
<dimen name="gestures_assistant_width">48dp</dimen>
|
||||
<dimen name="gestures_assistant_drag_threshold">55dp</dimen>
|
||||
|
||||
<!-- Distance to move elements when swiping up to go home from launcher -->
|
||||
|
||||
@@ -765,7 +765,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
LauncherAnimationRunner.AnimationResult result) {
|
||||
if (!mLauncher.hasBeenResumed()) {
|
||||
// If launcher is not resumed, wait until new async-frame after resume
|
||||
mLauncher.setOnResumeCallback(() ->
|
||||
mLauncher.addOnResumeCallback(() ->
|
||||
postAsyncCallback(mHandler, () ->
|
||||
onCreateAnimation(targetCompats, result)));
|
||||
return;
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
|
||||
/**
|
||||
* Definition for AllApps state
|
||||
@@ -63,7 +64,13 @@ public class AllAppsState extends LauncherState {
|
||||
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
|
||||
ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
|
||||
.getWorkspaceScaleAndTranslation(launcher);
|
||||
scaleAndTranslation.scale = 1;
|
||||
if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
|
||||
float normalScale = 1;
|
||||
// Scale down halfway to where we'd be in overview, to prepare for a potential pause.
|
||||
scaleAndTranslation.scale = (scaleAndTranslation.scale + normalScale) / 2;
|
||||
} else {
|
||||
scaleAndTranslation.scale = 1;
|
||||
}
|
||||
return scaleAndTranslation;
|
||||
}
|
||||
|
||||
|
||||
+51
-17
@@ -17,17 +17,22 @@ package com.android.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_MOVE;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.touch.TouchEventTranslator;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
@@ -36,18 +41,29 @@ import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* TouchController for handling touch events that get sent to the StatusBar. Once the
|
||||
* Once the event delta y passes the touch slop, the events start getting forwarded.
|
||||
* Once the event delta mDownY passes the touch slop, the events start getting forwarded.
|
||||
* All events are offset by initial Y value of the pointer.
|
||||
*/
|
||||
public class StatusBarTouchController implements TouchController {
|
||||
|
||||
private static final String TAG = "StatusBarController";
|
||||
|
||||
/**
|
||||
* Window flag: Enable touches to slide out of a window into neighboring
|
||||
* windows in mid-gesture instead of being captured for the duration of
|
||||
* the gesture.
|
||||
*
|
||||
* This flag changes the behavior of touch focus for this window only.
|
||||
* Touches can slide out of the window but they cannot necessarily slide
|
||||
* back in (unless the other window with touch focus permits it).
|
||||
*/
|
||||
private static final int FLAG_SLIPPERY = 0x20000000;
|
||||
|
||||
protected final Launcher mLauncher;
|
||||
protected final TouchEventTranslator mTranslator;
|
||||
private final float mTouchSlop;
|
||||
private ISystemUiProxy mSysUiProxy;
|
||||
private int mLastAction;
|
||||
private final SparseArray<PointF> mDownEvents;
|
||||
|
||||
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
|
||||
private boolean mCanIntercept;
|
||||
@@ -56,7 +72,7 @@ public class StatusBarTouchController implements TouchController {
|
||||
mLauncher = l;
|
||||
// Guard against TAPs by increasing the touch slop.
|
||||
mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
|
||||
mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
|
||||
mDownEvents = new SparseArray<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,7 +80,6 @@ public class StatusBarTouchController implements TouchController {
|
||||
writer.println(prefix + "mCanIntercept:" + mCanIntercept);
|
||||
writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
|
||||
writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
|
||||
|
||||
}
|
||||
|
||||
private void dispatchTouchEvent(MotionEvent ev) {
|
||||
@@ -81,26 +96,31 @@ public class StatusBarTouchController implements TouchController {
|
||||
@Override
|
||||
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
int action = ev.getActionMasked();
|
||||
int idx = ev.getActionIndex();
|
||||
int pid = ev.getPointerId(idx);
|
||||
if (action == ACTION_DOWN) {
|
||||
mCanIntercept = canInterceptTouch(ev);
|
||||
if (!mCanIntercept) {
|
||||
return false;
|
||||
}
|
||||
mTranslator.reset();
|
||||
mTranslator.setDownParameters(0, ev);
|
||||
mDownEvents.put(pid, new PointF(ev.getX(), ev.getY()));
|
||||
} else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
// Check!! should only set it only when threshold is not entered.
|
||||
mTranslator.setDownParameters(ev.getActionIndex(), ev);
|
||||
// Check!! should only set it only when threshold is not entered.
|
||||
mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx)));
|
||||
}
|
||||
if (!mCanIntercept) {
|
||||
return false;
|
||||
}
|
||||
if (action == ACTION_MOVE) {
|
||||
float dy = ev.getY() - mTranslator.getDownY();
|
||||
float dx = ev.getX() - mTranslator.getDownX();
|
||||
if (dy > mTouchSlop && dy > Math.abs(dx)) {
|
||||
mTranslator.dispatchDownEvents(ev);
|
||||
mTranslator.processMotionEvent(ev);
|
||||
float dy = ev.getY(idx) - mDownEvents.get(pid).y;
|
||||
float dx = ev.getX(idx) - mDownEvents.get(pid).x;
|
||||
// Currently input dispatcher will not do touch transfer if there are more than
|
||||
// one touch pointer. Hence, even if slope passed, only set the slippery flag
|
||||
// when there is single touch event. (context: InputDispatcher.cpp line 1445)
|
||||
if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) {
|
||||
ev.setAction(ACTION_DOWN);
|
||||
dispatchTouchEvent(ev);
|
||||
setWindowSlippery(true);
|
||||
return true;
|
||||
}
|
||||
if (Math.abs(dx) > mTouchSlop) {
|
||||
@@ -110,13 +130,27 @@ public class StatusBarTouchController implements TouchController {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean onControllerTouchEvent(MotionEvent ev) {
|
||||
mTranslator.processMotionEvent(ev);
|
||||
if (ev.getAction() == ACTION_UP || ev.getAction() == ACTION_CANCEL) {
|
||||
dispatchTouchEvent(ev);
|
||||
setWindowSlippery(false);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setWindowSlippery(boolean enable) {
|
||||
Window w = mLauncher.getWindow();
|
||||
WindowManager.LayoutParams wlp = w.getAttributes();
|
||||
if (enable) {
|
||||
wlp.flags |= FLAG_SLIPPERY;
|
||||
} else {
|
||||
wlp.flags &= ~FLAG_SLIPPERY;
|
||||
}
|
||||
w.setAttributes(wlp);
|
||||
}
|
||||
|
||||
private boolean canInterceptTouch(MotionEvent ev) {
|
||||
if (!mLauncher.isInState(LauncherState.NORMAL) ||
|
||||
AbstractFloatingView.getTopOpenViewWithType(mLauncher,
|
||||
@@ -132,4 +166,4 @@ public class StatusBarTouchController implements TouchController {
|
||||
mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy();
|
||||
return mSysUiProxy != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import android.content.pm.ResolveInfo;
|
||||
|
||||
import com.android.systemui.shared.system.PackageManagerWrapper;
|
||||
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -58,7 +57,9 @@ public final class OverviewComponentObserver {
|
||||
private String mUpdateRegisteredPackage;
|
||||
private ActivityControlHelper mActivityControlHelper;
|
||||
private Intent mOverviewIntent;
|
||||
private Intent mHomeIntent;
|
||||
private int mSystemUiStateFlags;
|
||||
private boolean mIsHomeAndOverviewSame;
|
||||
|
||||
public OverviewComponentObserver(Context context) {
|
||||
mContext = context;
|
||||
@@ -93,11 +94,14 @@ public final class OverviewComponentObserver {
|
||||
|
||||
final String overviewIntentCategory;
|
||||
ComponentName overviewComponent;
|
||||
mHomeIntent = null;
|
||||
|
||||
if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 &&
|
||||
(defaultHome == null || mMyHomeComponent.equals(defaultHome))) {
|
||||
// User default home is same as out home app. Use Overview integrated in Launcher.
|
||||
overviewComponent = mMyHomeComponent;
|
||||
mActivityControlHelper = new LauncherActivityControllerHelper();
|
||||
mIsHomeAndOverviewSame = true;
|
||||
overviewIntentCategory = Intent.CATEGORY_HOME;
|
||||
|
||||
if (mUpdateRegisteredPackage != null) {
|
||||
@@ -109,8 +113,12 @@ public final class OverviewComponentObserver {
|
||||
// The default home app is a different launcher. Use the fallback Overview instead.
|
||||
overviewComponent = new ComponentName(mContext, RecentsActivity.class);
|
||||
mActivityControlHelper = new FallbackActivityControllerHelper();
|
||||
mIsHomeAndOverviewSame = false;
|
||||
overviewIntentCategory = Intent.CATEGORY_DEFAULT;
|
||||
|
||||
mHomeIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setComponent(defaultHome);
|
||||
// User's default home app can change as a result of package updates of this app (such
|
||||
// as uninstalling the app or removing the "Launcher" feature in an update).
|
||||
// Listen for package updates of this app (and remove any previously attached
|
||||
@@ -135,6 +143,9 @@ public final class OverviewComponentObserver {
|
||||
.addCategory(overviewIntentCategory)
|
||||
.setComponent(overviewComponent)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (mHomeIntent == null) {
|
||||
mHomeIntent = mOverviewIntent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,6 +169,20 @@ public final class OverviewComponentObserver {
|
||||
return mOverviewIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current intent for going to the home activity.
|
||||
*/
|
||||
public Intent getHomeIntent() {
|
||||
return mHomeIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if home and overview are same activity.
|
||||
*/
|
||||
public boolean isHomeAndOverviewSame() {
|
||||
return mIsHomeAndOverviewSame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current activity control helper for managing interactions to the overview activity.
|
||||
*
|
||||
|
||||
+13
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.UserManager;
|
||||
@@ -22,17 +23,29 @@ import android.util.Log;
|
||||
|
||||
import com.android.launcher3.BuildConfig;
|
||||
import com.android.launcher3.MainProcessInitializer;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.systemui.shared.system.ThreadedRendererCompat;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class QuickstepProcessInitializer extends MainProcessInitializer {
|
||||
|
||||
private static final String TAG = "QuickstepProcessInitializer";
|
||||
private static final int HEAP_LIMIT_MB = 250;
|
||||
|
||||
public QuickstepProcessInitializer(Context context) { }
|
||||
|
||||
@Override
|
||||
protected void init(Context context) {
|
||||
if (Utilities.IS_DEBUG_DEVICE) {
|
||||
try {
|
||||
// Trigger a heap dump if the PSS reaches beyond the target heap limit
|
||||
final ActivityManager am = context.getSystemService(ActivityManager.class);
|
||||
am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024);
|
||||
} catch (SecurityException e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for b/120550382, an external app can cause the launcher process to start for
|
||||
// a work profile user which we do not support. Disable the application immediately when we
|
||||
// detect this to be the case.
|
||||
@@ -84,7 +84,7 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
final int requestLoadId = mChangeId;
|
||||
Runnable resultCallback = callback == null
|
||||
? () -> { }
|
||||
: () -> callback.accept(mTasks);
|
||||
: () -> callback.accept(copyOf(mTasks));
|
||||
|
||||
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
|
||||
// The list is up to date, callback with the same list
|
||||
@@ -183,4 +183,14 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
|
||||
return allTasks;
|
||||
}
|
||||
|
||||
private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
|
||||
ArrayList<Task> newTasks = new ArrayList<>();
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
Task t = tasks.get(i);
|
||||
newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable,
|
||||
t.isLocked, t.taskDescription, t.topActivity));
|
||||
}
|
||||
return newTasks;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class LayoutUtils {
|
||||
float y = insets.top + Math.max(topIconMargin,
|
||||
(launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
|
||||
outRect.set(Math.round(x), Math.round(y),
|
||||
Math.round(x + outWidth), Math.round(y + outHeight));
|
||||
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
|
||||
}
|
||||
|
||||
public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.quickstep.views;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
||||
|
||||
@@ -29,6 +30,7 @@ import android.graphics.Path;
|
||||
import android.graphics.Path.Direction;
|
||||
import android.graphics.Path.Op;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
@@ -69,6 +71,9 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
private int mMidAlpha;
|
||||
private float mMidProgress;
|
||||
|
||||
private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
|
||||
private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
|
||||
|
||||
private float mShiftRange;
|
||||
|
||||
private final float mShelfOffset;
|
||||
@@ -120,6 +125,15 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
@Override
|
||||
public void onNavigationModeChanged(Mode newMode) {
|
||||
mSysUINavigationMode = newMode;
|
||||
// Note that these interpolators are inverted because progress goes 1 to 0.
|
||||
if (mSysUINavigationMode == Mode.NO_BUTTON) {
|
||||
// Show the shelf more quickly before reaching overview progress.
|
||||
mBeforeMidProgressColorInterpolator = ACCEL_2;
|
||||
mAfterMidProgressColorInterpolator = ACCEL;
|
||||
} else {
|
||||
mBeforeMidProgressColorInterpolator = ACCEL;
|
||||
mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,7 +185,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
mRemainingScreenColor = 0;
|
||||
|
||||
int alpha = Math.round(Utilities.mapToRange(
|
||||
mProgress, mMidProgress, 1, mMidAlpha, 0, ACCEL));
|
||||
mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
|
||||
mShelfColor = setColorAlphaBound(mEndScrim, alpha);
|
||||
} else {
|
||||
mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
|
||||
@@ -179,7 +193,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
// Note that these ranges and interpolators are inverted because progress goes 1 to 0.
|
||||
int alpha = Math.round(
|
||||
Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
|
||||
(float) mMidAlpha, Interpolators.clampToProgress(ACCEL, 0.5f, 1f)));
|
||||
(float) mMidAlpha, mAfterMidProgressColorInterpolator));
|
||||
mShelfColor = setColorAlphaBound(mEndScrim, alpha);
|
||||
|
||||
int remainingScrimAlpha = Math.round(
|
||||
|
||||
@@ -43,10 +43,12 @@ import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.android.launcher3.tapl.LauncherInstrumentation;
|
||||
import com.android.launcher3.testcomponent.TestCommandReceiver;
|
||||
import com.android.launcher3.util.rule.FailureWatcher;
|
||||
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.Statement;
|
||||
@@ -62,10 +64,14 @@ public class FallbackRecentsTest {
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final ActivityInfo mOtherLauncherActivity;
|
||||
|
||||
@Rule public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification();
|
||||
@Rule public final TestRule mQuickstepOnOffExecutor;
|
||||
@Rule
|
||||
public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification();
|
||||
|
||||
@Rule public final TestRule mSetLauncherCommand;
|
||||
@Rule
|
||||
public final TestRule mSetLauncherCommand;
|
||||
|
||||
@Rule
|
||||
public final TestRule mOrderSensitiveRules;
|
||||
|
||||
public FallbackRecentsTest() throws RemoteException {
|
||||
Instrumentation instrumentation = getInstrumentation();
|
||||
@@ -74,7 +80,10 @@ public class FallbackRecentsTest {
|
||||
mDevice.setOrientationNatural();
|
||||
mLauncher = new LauncherInstrumentation(instrumentation);
|
||||
|
||||
mQuickstepOnOffExecutor = new NavigationModeSwitchRule(mLauncher);
|
||||
mOrderSensitiveRules = RuleChain.
|
||||
outerRule(new NavigationModeSwitchRule(mLauncher)).
|
||||
around(new FailureWatcher(mDevice));
|
||||
|
||||
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
|
||||
getHomeIntentInPackage(context),
|
||||
MATCH_DISABLED_COMPONENTS).get(0).activityInfo;
|
||||
|
||||
@@ -159,13 +159,20 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
}
|
||||
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
if (mLauncher.getNavigationModel() == expectedMode) {
|
||||
Thread.sleep(5000);
|
||||
return;
|
||||
}
|
||||
if (mLauncher.getNavigationModel() == expectedMode) break;
|
||||
Thread.sleep(100);
|
||||
}
|
||||
Assert.fail("Couldn't switch to " + overlayPackage);
|
||||
Assert.assertTrue("Couldn't switch to " + overlayPackage,
|
||||
mLauncher.getNavigationModel() == expectedMode);
|
||||
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
if (mLauncher.getNavigationModeMismatchError() == null) break;
|
||||
Thread.sleep(100);
|
||||
}
|
||||
final String error = mLauncher.getNavigationModeMismatchError();
|
||||
Assert.assertTrue("Switching nav mode: " + error, error == null);
|
||||
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
private void setOverlayPackageEnabled(String overlayPackage, boolean enable)
|
||||
|
||||
@@ -208,7 +208,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
|
||||
@Test
|
||||
@NavigationModeSwitch
|
||||
@PortraitLandscape
|
||||
// @PortraitLandscape
|
||||
public void testBackground() throws Exception {
|
||||
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
|
||||
final Background background = mLauncher.getBackground();
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.view.ViewDebug;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.Launcher.OnResumeCallback;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.DrawableFactory;
|
||||
@@ -227,6 +228,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
applyFromWorkspaceItem(info, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
|
||||
if (delegate instanceof LauncherAccessibilityDelegate) {
|
||||
super.setAccessibilityDelegate(delegate);
|
||||
} else {
|
||||
// NO-OP
|
||||
// Workaround for b/129745295 where RecyclerView is setting our Accessibility
|
||||
// delegate incorrectly. There are no cases when we shouldn't be using the
|
||||
// LauncherAccessibilityDelegate for BubbleTextView.
|
||||
}
|
||||
}
|
||||
|
||||
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
|
||||
applyIconAndLabel(info);
|
||||
setTag(info);
|
||||
|
||||
@@ -244,7 +244,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
|
||||
@Thunk boolean mWorkspaceLoading = true;
|
||||
|
||||
private OnResumeCallback mOnResumeCallback;
|
||||
private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
|
||||
|
||||
private ViewOnDrawExecutor mPendingExecutor;
|
||||
|
||||
@@ -430,6 +430,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
onIdpChanged(mDeviceProfile.inv);
|
||||
}
|
||||
|
||||
private boolean supportsFakeLandscapeUI() {
|
||||
return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate();
|
||||
}
|
||||
@@ -865,6 +869,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
if (mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onStop();
|
||||
}
|
||||
@@ -947,7 +952,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
mHandler.removeCallbacks(mHandleDeferredResume);
|
||||
Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
|
||||
|
||||
setOnResumeCallback(null);
|
||||
for (OnResumeCallback cb : mOnResumeCallbacks) {
|
||||
cb.onLauncherResume();
|
||||
}
|
||||
mOnResumeCallbacks.clear();
|
||||
|
||||
if (mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onResume();
|
||||
@@ -1801,6 +1809,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
android.util.Log.d(TestProtocol.NO_START_TAG,
|
||||
"startActivitySafely outer");
|
||||
}
|
||||
|
||||
if (!hasBeenResumed()) {
|
||||
// Workaround an issue where the WM launch animation is clobbered when finishing the
|
||||
// recents animation into launcher. Defer launching the activity until Launcher is
|
||||
// next resumed.
|
||||
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
|
||||
UiFactory.clearSwipeSharedState(true /* finishAnimation */);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
|
||||
if (success && v instanceof BubbleTextView) {
|
||||
// This is set to the view that launched the activity that navigated the user away
|
||||
@@ -1809,7 +1827,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
// state when we return to launcher.
|
||||
BubbleTextView btv = (BubbleTextView) v;
|
||||
btv.setStayPressed(true);
|
||||
setOnResumeCallback(btv);
|
||||
addOnResumeCallback(btv);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -1857,11 +1875,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setOnResumeCallback(OnResumeCallback callback) {
|
||||
if (mOnResumeCallback != null) {
|
||||
mOnResumeCallback.onLauncherResume();
|
||||
}
|
||||
mOnResumeCallback = callback;
|
||||
public void addOnResumeCallback(OnResumeCallback callback) {
|
||||
mOnResumeCallbacks.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.Property;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
@@ -47,15 +48,15 @@ public class LauncherAnimUtils {
|
||||
}
|
||||
};
|
||||
|
||||
public static final Property<View, Float> SCALE_PROPERTY =
|
||||
new Property<View, Float>(Float.class, "scale") {
|
||||
public static final FloatProperty<View> SCALE_PROPERTY =
|
||||
new FloatProperty<View>("scale") {
|
||||
@Override
|
||||
public Float get(View view) {
|
||||
return view.getScaleX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(View view, Float scale) {
|
||||
public void setValue(View view, float scale) {
|
||||
view.setScaleX(scale);
|
||||
view.setScaleY(scale);
|
||||
}
|
||||
@@ -92,23 +93,31 @@ public class LauncherAnimUtils {
|
||||
}
|
||||
};
|
||||
|
||||
public static class ViewProgressProperty implements ProgressInterface {
|
||||
View mView;
|
||||
Property<View, Float> mProperty;
|
||||
public static final FloatProperty<View> VIEW_TRANSLATE_X =
|
||||
View.TRANSLATION_X instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_X
|
||||
: new FloatProperty<View>("translateX") {
|
||||
@Override
|
||||
public void setValue(View view, float v) {
|
||||
view.setTranslationX(v);
|
||||
}
|
||||
|
||||
public ViewProgressProperty(View view, Property<View, Float> property) {
|
||||
mView = view;
|
||||
mProperty = property;
|
||||
}
|
||||
@Override
|
||||
public Float get(View view) {
|
||||
return view.getTranslationX();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void setProgress(float progress) {
|
||||
mProperty.set(mView, progress);
|
||||
}
|
||||
public static final FloatProperty<View> VIEW_TRANSLATE_Y =
|
||||
View.TRANSLATION_Y instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_Y
|
||||
: new FloatProperty<View>("translateY") {
|
||||
@Override
|
||||
public void setValue(View view, float v) {
|
||||
view.setTranslationY(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getProgress() {
|
||||
return mProperty.get(mView);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Float get(View view) {
|
||||
return view.getTranslationY();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
@@ -55,4 +56,15 @@ public class LauncherAppTransitionManager implements ResourceBasedOverride {
|
||||
public boolean supportsAdaptiveIconAnimation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of animations which run on state properties.
|
||||
*/
|
||||
public int getStateElementAnimationsCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Animator createStateElementAnimation(int index, float... values) {
|
||||
throw new RuntimeException("Unknown gesture animation " + index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,8 @@ public class LauncherModel extends BroadcastReceiver
|
||||
}
|
||||
}
|
||||
} else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
|
||||
forceReload();
|
||||
Launcher l = (Launcher) getCallback();
|
||||
l.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,18 @@ package com.android.launcher3;
|
||||
|
||||
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
|
||||
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
|
||||
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
|
||||
import static com.android.launcher3.anim.Interpolators.clampToProgress;
|
||||
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
|
||||
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
|
||||
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
|
||||
@@ -30,11 +41,11 @@ import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.states.SpringLoadedState;
|
||||
import com.android.launcher3.uioverrides.UiFactory;
|
||||
import com.android.launcher3.uioverrides.states.AllAppsState;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -272,6 +283,46 @@ public class LauncherState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares for a non-user controlled animation from fromState to this state. Preparations
|
||||
* include:
|
||||
* - Setting interpolators for various animations included in the state transition.
|
||||
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
|
||||
*/
|
||||
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
|
||||
AnimatorSetBuilder builder) {
|
||||
if (this == NORMAL && fromState == OVERVIEW) {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
|
||||
Workspace workspace = launcher.getWorkspace();
|
||||
|
||||
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
|
||||
boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
|
||||
if (isWorkspaceVisible) {
|
||||
CellLayout currentChild = (CellLayout) workspace.getChildAt(
|
||||
workspace.getCurrentPage());
|
||||
isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
|
||||
&& currentChild.getShortcutsAndWidgets().getAlpha() > 0;
|
||||
}
|
||||
if (!isWorkspaceVisible) {
|
||||
workspace.setScaleX(0.92f);
|
||||
workspace.setScaleY(0.92f);
|
||||
}
|
||||
Hotseat hotseat = launcher.getHotseat();
|
||||
boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
|
||||
if (!isHotseatVisible) {
|
||||
hotseat.setScaleX(0.92f);
|
||||
hotseat.setScaleY(0.92f);
|
||||
}
|
||||
} else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
|
||||
// Keep fully visible until the very end (when overview is offscreen) to make invisible.
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void dispatchWindowStateChanged(Launcher launcher) {
|
||||
launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
|
||||
}
|
||||
|
||||
@@ -16,23 +16,7 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
import static com.android.launcher3.anim.Interpolators.clampToProgress;
|
||||
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -42,8 +26,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
@@ -58,6 +40,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
/**
|
||||
* TODO: figure out what kind of tests we can write for this
|
||||
*
|
||||
@@ -127,6 +111,9 @@ public class LauncherStateManager {
|
||||
private final Launcher mLauncher;
|
||||
private final ArrayList<StateListener> mListeners = new ArrayList<>();
|
||||
|
||||
// Animators which are run on properties also controlled by state animations.
|
||||
private Animator[] mStateElementAnimators;
|
||||
|
||||
private StateHandler[] mStateHandlers;
|
||||
private LauncherState mState = NORMAL;
|
||||
|
||||
@@ -223,13 +210,18 @@ public class LauncherStateManager {
|
||||
}
|
||||
|
||||
public void reapplyState(boolean cancelCurrentAnimation) {
|
||||
boolean wasInAnimation = mConfig.mCurrentAnimation != null;
|
||||
if (cancelCurrentAnimation) {
|
||||
cancelAllStateElementAnimation();
|
||||
cancelAnimation();
|
||||
}
|
||||
if (mConfig.mCurrentAnimation == null) {
|
||||
for (StateHandler handler : getStateHandlers()) {
|
||||
handler.setState(mState);
|
||||
}
|
||||
if (wasInAnimation) {
|
||||
onStateTransitionEnd(mState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +254,7 @@ public class LauncherStateManager {
|
||||
mConfig.reset();
|
||||
|
||||
if (!animated) {
|
||||
cancelAllStateElementAnimation();
|
||||
onStateTransitionStart(state);
|
||||
for (StateHandler handler : getStateHandlers()) {
|
||||
handler.setState(state);
|
||||
@@ -310,47 +303,7 @@ public class LauncherStateManager {
|
||||
*/
|
||||
public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
|
||||
AnimatorSetBuilder builder) {
|
||||
if (fromState == NORMAL && toState == OVERVIEW) {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
|
||||
|
||||
// Start from a higher overview scale, but only if we're invisible so we don't jump.
|
||||
UiFactory.prepareToShowOverview(mLauncher);
|
||||
} else if (fromState == OVERVIEW && toState == NORMAL) {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
|
||||
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
|
||||
boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
|
||||
if (isWorkspaceVisible) {
|
||||
CellLayout currentChild = (CellLayout) workspace.getChildAt(
|
||||
workspace.getCurrentPage());
|
||||
isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
|
||||
&& currentChild.getShortcutsAndWidgets().getAlpha() > 0;
|
||||
}
|
||||
if (!isWorkspaceVisible) {
|
||||
workspace.setScaleX(0.92f);
|
||||
workspace.setScaleY(0.92f);
|
||||
}
|
||||
Hotseat hotseat = workspace.getHotseat();
|
||||
boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
|
||||
if (!isHotseatVisible) {
|
||||
hotseat.setScaleX(0.92f);
|
||||
hotseat.setScaleY(0.92f);
|
||||
}
|
||||
} else if (fromState == NORMAL && toState == OVERVIEW_PEEK) {
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
|
||||
} else if (fromState == OVERVIEW_PEEK && toState == NORMAL) {
|
||||
// Keep fully visible until the very end (when overview is offscreen) to make invisible.
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
|
||||
}
|
||||
toState.prepareForAtomicAnimation(mLauncher, fromState, builder);
|
||||
}
|
||||
|
||||
public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
|
||||
@@ -573,6 +526,47 @@ public class LauncherStateManager {
|
||||
mConfig.setAnimation(anim, null);
|
||||
}
|
||||
|
||||
private void cancelAllStateElementAnimation() {
|
||||
if (mStateElementAnimators == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Animator animator : mStateElementAnimators) {
|
||||
if (animator != null) {
|
||||
animator.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a currently running gesture animation
|
||||
*/
|
||||
public void cancelStateElementAnimation(int index) {
|
||||
if (mStateElementAnimators == null) {
|
||||
return;
|
||||
}
|
||||
if (mStateElementAnimators[index] != null) {
|
||||
mStateElementAnimators[index].cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public Animator createStateElementAnimation(int index, float... values) {
|
||||
cancelStateElementAnimation(index);
|
||||
LauncherAppTransitionManager latm = mLauncher.getAppTransitionManager();
|
||||
if (mStateElementAnimators == null) {
|
||||
mStateElementAnimators = new Animator[latm.getStateElementAnimationsCount()];
|
||||
}
|
||||
Animator anim = latm.createStateElementAnimation(index, values);
|
||||
mStateElementAnimators[index] = anim;
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mStateElementAnimators[index] = null;
|
||||
}
|
||||
});
|
||||
return anim;
|
||||
}
|
||||
|
||||
private void clearCurrentAnimation() {
|
||||
if (mConfig.mCurrentAnimation != null) {
|
||||
mConfig.mCurrentAnimation.removeListener(mConfig);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.Utilities.shouldDisableGestures;
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
|
||||
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
|
||||
@@ -844,10 +845,11 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
* If we return true, onTouchEvent will be called and we do the actual
|
||||
* scrolling there.
|
||||
*/
|
||||
acquireVelocityTrackerAndAddMovement(ev);
|
||||
|
||||
// Skip touch handling if there are no pages to swipe
|
||||
if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
|
||||
if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
|
||||
|
||||
acquireVelocityTrackerAndAddMovement(ev);
|
||||
|
||||
/*
|
||||
* Shortcut the most recurring case: the user is in the dragging
|
||||
@@ -1093,7 +1095,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
// Skip touch handling if there are no pages to swipe
|
||||
if (getChildCount() <= 0) return false;
|
||||
if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
|
||||
|
||||
acquireVelocityTrackerAndAddMovement(ev);
|
||||
|
||||
@@ -1204,6 +1206,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) ||
|
||||
((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) {
|
||||
mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX);
|
||||
mNextPage = getPageNearestToCenterOfScreen();
|
||||
} else {
|
||||
mScroller.setInterpolator(mDefaultInterpolator);
|
||||
mScroller.fling(initialScrollX, -velocityX,
|
||||
|
||||
@@ -179,7 +179,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
|
||||
DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
|
||||
if (target != null) {
|
||||
deferred.mPackageName = target.getPackageName();
|
||||
mLauncher.setOnResumeCallback(deferred);
|
||||
mLauncher.addOnResumeCallback(deferred);
|
||||
} else {
|
||||
deferred.sendFailure();
|
||||
}
|
||||
|
||||
@@ -127,6 +127,16 @@ public final class Utilities {
|
||||
*/
|
||||
public static final int EDGE_NAV_BAR = 1 << 8;
|
||||
|
||||
/**
|
||||
* Set on a motion event do disallow any gestures and only handle touch.
|
||||
* See {@link MotionEvent#setEdgeFlags(int)}.
|
||||
*/
|
||||
public static final int FLAG_NO_GESTURES = 1 << 9;
|
||||
|
||||
public static boolean shouldDisableGestures(MotionEvent ev) {
|
||||
return (ev.getEdgeFlags() & FLAG_NO_GESTURES) == FLAG_NO_GESTURES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the device has a debug build. Should only be used to store additional info or
|
||||
* add extra logging and not for changing the app behavior.
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.launcher3;
|
||||
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
@@ -104,7 +106,10 @@ public class WorkspaceStateTransitionAnimation {
|
||||
hotseat.setPivotY(workspacePivot[1]);
|
||||
}
|
||||
float hotseatScale = hotseatScaleAndTranslation.scale;
|
||||
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator);
|
||||
Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE,
|
||||
scaleInterpolator);
|
||||
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
|
||||
hotseatScaleInterpolator);
|
||||
|
||||
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
|
||||
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
|
||||
@@ -125,10 +130,12 @@ public class WorkspaceStateTransitionAnimation {
|
||||
propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
|
||||
scaleAndTranslation.translationY, translationInterpolator);
|
||||
|
||||
Interpolator hotseatTranslationInterpolator = builder.getInterpolator(
|
||||
ANIM_HOTSEAT_TRANSLATE, translationInterpolator);
|
||||
propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
|
||||
hotseatScaleAndTranslation.translationY, translationInterpolator);
|
||||
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
|
||||
propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
|
||||
hotseatScaleAndTranslation.translationY, translationInterpolator);
|
||||
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
|
||||
|
||||
setScrim(propertySetter, state);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.android.launcher3.allapps;
|
||||
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
|
||||
@@ -14,8 +16,8 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -24,19 +26,15 @@ import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationConfig;
|
||||
import com.android.launcher3.LauncherStateManager.StateHandler;
|
||||
import com.android.launcher3.ProgressInterface;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
|
||||
/**
|
||||
* Handles AllApps view transition.
|
||||
* 1) Slides all apps view using direct manipulation
|
||||
@@ -47,14 +45,13 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
|
||||
* closer to top or closer to the page indicator.
|
||||
*/
|
||||
public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener,
|
||||
ProgressInterface {
|
||||
public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
|
||||
|
||||
public static final float SPRING_DAMPING_RATIO = 0.9f;
|
||||
public static final float SPRING_STIFFNESS = 600f;
|
||||
private static final float SPRING_DAMPING_RATIO = 0.9f;
|
||||
private static final float SPRING_STIFFNESS = 600f;
|
||||
|
||||
public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
|
||||
new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
|
||||
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
|
||||
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
|
||||
|
||||
@Override
|
||||
public Float get(AllAppsTransitionController controller) {
|
||||
@@ -62,7 +59,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(AllAppsTransitionController controller, Float progress) {
|
||||
public void setValue(AllAppsTransitionController controller, float progress) {
|
||||
controller.setProgress(progress);
|
||||
}
|
||||
};
|
||||
@@ -122,7 +119,6 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
||||
* @see #setState(LauncherState)
|
||||
* @see #setStateWithAnimation(LauncherState, AnimatorSetBuilder, AnimationConfig)
|
||||
*/
|
||||
@Override
|
||||
public void setProgress(float progress) {
|
||||
mProgress = progress;
|
||||
mScrimView.setProgress(progress);
|
||||
@@ -139,9 +135,17 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
||||
} else {
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
|
||||
}
|
||||
|
||||
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
|
||||
// Translate hotseat with the shelf until reaching overview.
|
||||
float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
|
||||
if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
|
||||
float hotseatShift = (progress - overviewProgress) * mShiftRange;
|
||||
mLauncher.getHotseat().setTranslationY(hotseatShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
@@ -184,8 +188,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
||||
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
|
||||
? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
|
||||
: FAST_OUT_SLOW_IN;
|
||||
Animator anim = new SpringObjectAnimator<>(this, "allAppsSpringFromAATC", 1f / mShiftRange,
|
||||
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, mProgress, targetProgress);
|
||||
Animator anim = createSpringAnimation(mProgress, targetProgress);
|
||||
anim.setDuration(config.duration);
|
||||
anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
|
||||
anim.addListener(getProgressAnimatorListener());
|
||||
@@ -195,6 +198,11 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
||||
setAlphas(toState, config, builder);
|
||||
}
|
||||
|
||||
public Animator createSpringAnimation(float... progressValues) {
|
||||
return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
|
||||
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
|
||||
}
|
||||
|
||||
private void setAlphas(LauncherState toState, AnimationConfig config,
|
||||
AnimatorSetBuilder builder) {
|
||||
setAlphas(toState.getVisibleElements(mLauncher), config, builder);
|
||||
|
||||
@@ -32,11 +32,13 @@ public class AnimatorSetBuilder {
|
||||
public static final int ANIM_WORKSPACE_SCALE = 1;
|
||||
public static final int ANIM_WORKSPACE_TRANSLATE = 2;
|
||||
public static final int ANIM_WORKSPACE_FADE = 3;
|
||||
public static final int ANIM_OVERVIEW_SCALE = 4;
|
||||
public static final int ANIM_OVERVIEW_TRANSLATE_X = 5;
|
||||
public static final int ANIM_OVERVIEW_TRANSLATE_Y = 6;
|
||||
public static final int ANIM_OVERVIEW_FADE = 7;
|
||||
public static final int ANIM_ALL_APPS_FADE = 8;
|
||||
public static final int ANIM_HOTSEAT_SCALE = 4;
|
||||
public static final int ANIM_HOTSEAT_TRANSLATE = 5;
|
||||
public static final int ANIM_OVERVIEW_SCALE = 6;
|
||||
public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
|
||||
public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
|
||||
public static final int ANIM_OVERVIEW_FADE = 9;
|
||||
public static final int ANIM_ALL_APPS_FADE = 10;
|
||||
|
||||
public static final int FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0;
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.launcher3.anim;
|
||||
|
||||
import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -24,15 +26,12 @@ import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
|
||||
import com.android.launcher3.ProgressInterface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
@@ -40,12 +39,11 @@ import androidx.dynamicanimation.animation.SpringForce;
|
||||
* This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
|
||||
* a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
|
||||
*/
|
||||
public class SpringObjectAnimator<T extends ProgressInterface> extends ValueAnimator {
|
||||
public class SpringObjectAnimator<T> extends ValueAnimator {
|
||||
|
||||
private static final String TAG = "SpringObjectAnimator";
|
||||
private static boolean DEBUG = false;
|
||||
|
||||
private T mObject;
|
||||
private ObjectAnimator mObjectAnimator;
|
||||
private float[] mValues;
|
||||
|
||||
@@ -57,29 +55,15 @@ public class SpringObjectAnimator<T extends ProgressInterface> extends ValueAnim
|
||||
private boolean mAnimatorEnded = true;
|
||||
private boolean mEnded = true;
|
||||
|
||||
private static final FloatPropertyCompat<ProgressInterface> sFloatProperty =
|
||||
new FloatPropertyCompat<ProgressInterface>("springObjectAnimator") {
|
||||
@Override
|
||||
public float getValue(ProgressInterface object) {
|
||||
return object.getProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ProgressInterface object, float progress) {
|
||||
object.setProgress(progress);
|
||||
}
|
||||
};
|
||||
|
||||
public SpringObjectAnimator(T object, String name, float minimumVisibleChange, float damping,
|
||||
float stiffness, float... values) {
|
||||
mObject = object;
|
||||
mSpring = new SpringAnimation(object, sFloatProperty);
|
||||
public SpringObjectAnimator(T object, FloatProperty<T> property, float minimumVisibleChange,
|
||||
float damping, float stiffness, float... values) {
|
||||
mSpring = new SpringAnimation(object, createFloatPropertyCompat(property));
|
||||
mSpring.setMinimumVisibleChange(minimumVisibleChange);
|
||||
mSpring.setSpring(new SpringForce(0)
|
||||
.setDampingRatio(damping)
|
||||
.setStiffness(stiffness));
|
||||
mSpring.setStartVelocity(0.01f);
|
||||
mProperty = new SpringProperty<T>(name, mSpring);
|
||||
mProperty = new SpringProperty<>(property, mSpring);
|
||||
mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
|
||||
mValues = values;
|
||||
mListeners = new ArrayList<>();
|
||||
@@ -285,13 +269,15 @@ public class SpringObjectAnimator<T extends ProgressInterface> extends ValueAnim
|
||||
mObjectAnimator.setCurrentPlayTime(playTime);
|
||||
}
|
||||
|
||||
public static class SpringProperty<T extends ProgressInterface> extends Property<T, Float> {
|
||||
public static class SpringProperty<T> extends FloatProperty<T> {
|
||||
|
||||
boolean useSpring = false;
|
||||
final FloatProperty<T> mProperty;
|
||||
final SpringAnimation mSpring;
|
||||
|
||||
public SpringProperty(String name, SpringAnimation spring) {
|
||||
super(Float.class, name);
|
||||
public SpringProperty(FloatProperty<T> property, SpringAnimation spring) {
|
||||
super(property.getName());
|
||||
mProperty = property;
|
||||
mSpring = spring;
|
||||
}
|
||||
|
||||
@@ -301,15 +287,15 @@ public class SpringObjectAnimator<T extends ProgressInterface> extends ValueAnim
|
||||
|
||||
@Override
|
||||
public Float get(T object) {
|
||||
return object.getProgress();
|
||||
return mProperty.get(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T object, Float progress) {
|
||||
public void setValue(T object, float progress) {
|
||||
if (useSpring) {
|
||||
mSpring.animateToFinalPosition(progress);
|
||||
} else {
|
||||
object.setProgress(progress);
|
||||
mProperty.setValue(object, progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ abstract class BaseFlags {
|
||||
"Show chip hints and gleams on the overview screen");
|
||||
|
||||
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
|
||||
"FAKE_LANDSCAPE_UI", true,
|
||||
"FAKE_LANDSCAPE_UI", false,
|
||||
"Rotate launcher UI instead of using transposed layout");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.touch;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.MotionEvent.PointerCoords;
|
||||
import android.view.MotionEvent.PointerProperties;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* To minimize the size of the MotionEvent, historic events are not copied and passed via the
|
||||
* listener.
|
||||
*/
|
||||
public class TouchEventTranslator {
|
||||
|
||||
private static final String TAG = "TouchEventTranslator";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private class DownState {
|
||||
long timeStamp;
|
||||
float downX;
|
||||
float downY;
|
||||
public DownState(long timeStamp, float downX, float downY) {
|
||||
this.timeStamp = timeStamp;
|
||||
this.downX = downX;
|
||||
this.downY = downY;
|
||||
}
|
||||
};
|
||||
private final DownState ZERO = new DownState(0, 0f, 0f);
|
||||
|
||||
private final Consumer<MotionEvent> mListener;
|
||||
|
||||
private final SparseArray<DownState> mDownEvents;
|
||||
private final SparseArray<PointF> mFingers;
|
||||
|
||||
private final SparseArray<Pair<PointerProperties[], PointerCoords[]>> mCache;
|
||||
|
||||
public TouchEventTranslator(Consumer<MotionEvent> listener) {
|
||||
mDownEvents = new SparseArray<>();
|
||||
mFingers = new SparseArray<>();
|
||||
mCache = new SparseArray<>();
|
||||
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mDownEvents.clear();
|
||||
mFingers.clear();
|
||||
}
|
||||
|
||||
public float getDownX() {
|
||||
return mDownEvents.get(0).downX;
|
||||
}
|
||||
|
||||
public float getDownY() {
|
||||
return mDownEvents.get(0).downY;
|
||||
}
|
||||
|
||||
public void setDownParameters(int idx, MotionEvent e) {
|
||||
DownState ev = new DownState(e.getEventTime(), e.getX(idx), e.getY(idx));
|
||||
mDownEvents.append(idx, ev);
|
||||
}
|
||||
|
||||
public void dispatchDownEvents(MotionEvent ev) {
|
||||
for(int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) {
|
||||
int pid = ev.getPointerId(i);
|
||||
put(pid, i, ev.getX(i), 0, mDownEvents.get(i).timeStamp, ev);
|
||||
}
|
||||
}
|
||||
|
||||
public void processMotionEvent(MotionEvent ev) {
|
||||
if (DEBUG) {
|
||||
printSamples(TAG + " processMotionEvent", ev);
|
||||
}
|
||||
int index = ev.getActionIndex();
|
||||
float x = ev.getX(index);
|
||||
float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY;
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
int pid = ev.getPointerId(index);
|
||||
if(mFingers.get(pid, null) != null) {
|
||||
for(int i=0; i < ev.getPointerCount(); i++) {
|
||||
pid = ev.getPointerId(i);
|
||||
position(pid, x, y);
|
||||
}
|
||||
generateEvent(ev.getAction(), ev);
|
||||
} else {
|
||||
put(pid, index, x, y, ev);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for(int i=0; i < ev.getPointerCount(); i++) {
|
||||
pid = ev.getPointerId(i);
|
||||
position(pid, x, y);
|
||||
}
|
||||
generateEvent(ev.getAction(), ev);
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_UP:
|
||||
pid = ev.getPointerId(index);
|
||||
lift(pid, index, x, y, ev);
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
cancel(ev);
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "Didn't process ");
|
||||
printSamples(TAG, ev);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private TouchEventTranslator put(int id, int index, float x, float y, MotionEvent ev) {
|
||||
return put(id, index, x, y, ev.getEventTime(), ev);
|
||||
}
|
||||
|
||||
private TouchEventTranslator put(int id, int index, float x, float y, long ms, MotionEvent ev) {
|
||||
checkFingerExistence(id, false);
|
||||
boolean isInitialDown = (mFingers.size() == 0);
|
||||
|
||||
mFingers.put(id, new PointF(x, y));
|
||||
int n = mFingers.size();
|
||||
|
||||
if (mCache.get(n) == null) {
|
||||
PointerProperties[] properties = new PointerProperties[n];
|
||||
PointerCoords[] coords = new PointerCoords[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
properties[i] = new PointerProperties();
|
||||
coords[i] = new PointerCoords();
|
||||
}
|
||||
mCache.put(n, new Pair(properties, coords));
|
||||
}
|
||||
|
||||
int action;
|
||||
if (isInitialDown) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else {
|
||||
action = MotionEvent.ACTION_POINTER_DOWN;
|
||||
// Set the id of the changed pointer.
|
||||
action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
}
|
||||
generateEvent(action, ms, ev);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TouchEventTranslator position(int id, float x, float y) {
|
||||
checkFingerExistence(id, true);
|
||||
mFingers.get(id).set(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
private TouchEventTranslator lift(int id, int index, MotionEvent ev) {
|
||||
checkFingerExistence(id, true);
|
||||
boolean isFinalUp = (mFingers.size() == 1);
|
||||
int action;
|
||||
if (isFinalUp) {
|
||||
action = MotionEvent.ACTION_UP;
|
||||
} else {
|
||||
action = MotionEvent.ACTION_POINTER_UP;
|
||||
// Set the id of the changed pointer.
|
||||
action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
}
|
||||
generateEvent(action, ev);
|
||||
mFingers.remove(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
private TouchEventTranslator lift(int id, int index, float x, float y, MotionEvent ev) {
|
||||
checkFingerExistence(id, true);
|
||||
mFingers.get(id).set(x, y);
|
||||
return lift(id, index, ev);
|
||||
}
|
||||
|
||||
public TouchEventTranslator cancel(MotionEvent ev) {
|
||||
generateEvent(MotionEvent.ACTION_CANCEL, ev);
|
||||
mFingers.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void checkFingerExistence(int id, boolean shouldExist) {
|
||||
if (shouldExist != (mFingers.get(id, null) != null)) {
|
||||
throw new IllegalArgumentException(
|
||||
shouldExist ? "Finger does not exist" : "Finger already exists");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to debug MotionEvents being sent/received.
|
||||
*/
|
||||
public void printSamples(String msg, MotionEvent ev) {
|
||||
System.out.printf("%s %s", msg, MotionEvent.actionToString(ev.getActionMasked()));
|
||||
final int pointerCount = ev.getPointerCount();
|
||||
System.out.printf("#%d/%d", ev.getActionIndex(), pointerCount);
|
||||
System.out.printf(" t=%d:", ev.getEventTime());
|
||||
for (int p = 0; p < pointerCount; p++) {
|
||||
System.out.printf(" id=%d: (%f,%f)",
|
||||
ev.getPointerId(p), ev.getX(p), ev.getY(p));
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private void generateEvent(int action, MotionEvent ev) {
|
||||
generateEvent(action, ev.getEventTime(), ev);
|
||||
}
|
||||
|
||||
private void generateEvent(int action, long ms, MotionEvent ev) {
|
||||
Pair<PointerProperties[], PointerCoords[]> state = getFingerState();
|
||||
MotionEvent event = MotionEvent.obtain(
|
||||
mDownEvents.get(0).timeStamp,
|
||||
ms,
|
||||
action,
|
||||
state.first.length,
|
||||
state.first,
|
||||
state.second,
|
||||
ev.getMetaState(),
|
||||
ev.getButtonState() /* buttonState */,
|
||||
ev.getXPrecision() /* xPrecision */,
|
||||
ev.getYPrecision() /* yPrecision */,
|
||||
ev.getDeviceId(),
|
||||
ev.getEdgeFlags(),
|
||||
ev.getSource(),
|
||||
ev.getFlags() /* flags */);
|
||||
if (DEBUG) {
|
||||
printSamples(TAG + " generateEvent", event);
|
||||
}
|
||||
if (event.getPointerId(event.getActionIndex()) < 0) {
|
||||
printSamples(TAG + "generateEvent", event);
|
||||
throw new IllegalStateException(event.getActionIndex() + " not found in MotionEvent");
|
||||
}
|
||||
mListener.accept(event);
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the fingers' state expected by MotionEvent.
|
||||
*/
|
||||
private Pair<PointerProperties[], PointerCoords[]> getFingerState() {
|
||||
int nFingers = mFingers.size();
|
||||
|
||||
Pair<PointerProperties[], PointerCoords[]> result = mCache.get(nFingers);
|
||||
PointerProperties[] properties = result.first;
|
||||
PointerCoords[] coordinates = result.second;
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < mFingers.size(); i++) {
|
||||
int id = mFingers.keyAt(i);
|
||||
PointF location = mFingers.get(id);
|
||||
|
||||
PointerProperties property = properties[i];
|
||||
property.id = id;
|
||||
property.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||
properties[index] = property;
|
||||
|
||||
PointerCoords coordinate = coordinates[i];
|
||||
coordinate.x = location.x;
|
||||
coordinate.y = location.y;
|
||||
coordinate.pressure = 1.0f;
|
||||
coordinates[index] = coordinate;
|
||||
|
||||
index++;
|
||||
}
|
||||
return mCache.get(nFingers);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
|
||||
import static com.android.launcher3.Utilities.shouldDisableGestures;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
@@ -116,11 +117,25 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #isEventOverView(View, MotionEvent, View)} where evView == this drag layer.
|
||||
*/
|
||||
public boolean isEventOverView(View view, MotionEvent ev) {
|
||||
getDescendantRectRelativeToSelf(view, mHitRect);
|
||||
return mHitRect.contains((int) ev.getX(), (int) ev.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a motion event in evView's coordinates, return whether the event is within another
|
||||
* view's bounds.
|
||||
*/
|
||||
public boolean isEventOverView(View view, MotionEvent ev, View evView) {
|
||||
int[] xy = new int[] {(int) ev.getX(), (int) ev.getY()};
|
||||
getDescendantCoordRelativeToSelf(evView, xy);
|
||||
getDescendantRectRelativeToSelf(view, mHitRect);
|
||||
return mHitRect.contains(xy[0], xy[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
int action = ev.getAction();
|
||||
@@ -137,6 +152,8 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
}
|
||||
|
||||
private TouchController findControllerToHandleTouch(MotionEvent ev) {
|
||||
if (shouldDisableGestures(ev)) return null;
|
||||
|
||||
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
|
||||
if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
|
||||
return topView;
|
||||
|
||||
@@ -69,8 +69,6 @@ public class UiFactory {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void prepareToShowOverview(Launcher launcher) { }
|
||||
|
||||
public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { }
|
||||
|
||||
|
||||
@@ -95,4 +93,6 @@ public class UiFactory {
|
||||
|
||||
public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
|
||||
|
||||
public static void clearSwipeSharedState(boolean finishAnimation) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.uiautomator.By;
|
||||
@@ -67,7 +66,6 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -124,46 +122,10 @@ public abstract class AbstractLauncherUiTest {
|
||||
protected @interface PortraitLandscape {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestRule mPortraitLandscapeExecutor =
|
||||
(base, description) -> false && description.getAnnotation(PortraitLandscape.class)
|
||||
!= null ? new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
// Create launcher activity if necessary and bring it to the front.
|
||||
mLauncher.pressHome();
|
||||
waitForLauncherCondition("Launcher activity wasn't created",
|
||||
launcher -> launcher != null);
|
||||
|
||||
executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(true));
|
||||
|
||||
evaluateInPortrait();
|
||||
evaluateInLandscape();
|
||||
} finally {
|
||||
mDevice.setOrientationNatural();
|
||||
executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(false));
|
||||
mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateInPortrait() throws Throwable {
|
||||
mDevice.setOrientationNatural();
|
||||
mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
base.evaluate();
|
||||
}
|
||||
|
||||
private void evaluateInLandscape() throws Throwable {
|
||||
mDevice.setOrientationLeft();
|
||||
mLauncher.setExpectedRotation(Surface.ROTATION_90);
|
||||
base.evaluate();
|
||||
}
|
||||
} : base;
|
||||
|
||||
protected TestRule getRulesInsideActivityMonitor() {
|
||||
return new FailureWatcher(this);
|
||||
return RuleChain.
|
||||
outerRule(new PortraitLandscapeRunner(this)).
|
||||
around(new FailureWatcher(mDevice));
|
||||
}
|
||||
|
||||
@Rule
|
||||
|
||||
@@ -25,26 +25,23 @@ import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.testcomponent.TestCommandReceiver;
|
||||
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||
import com.android.launcher3.util.rule.ShellCommandRule;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import androidx.test.uiautomator.UiSelector;
|
||||
|
||||
@MediumTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
@@ -71,7 +68,6 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
// Convert test to TAPL; b/131116002
|
||||
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
|
||||
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
|
||||
|
||||
@@ -79,14 +75,10 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
mActivityMonitor.startLauncher();
|
||||
waitForModelLoaded();
|
||||
|
||||
// Verify widget present
|
||||
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
|
||||
.description(getSettingsApp().getLabel().toString());
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
|
||||
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
// Convert test to TAPL; b/131116002
|
||||
public void testCustomProfileLoaded_with_widget() throws Exception {
|
||||
// A non-restored widget with no config screen gets restored automatically.
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
||||
@@ -100,13 +92,11 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
waitForModelLoaded();
|
||||
|
||||
// Verify widget present
|
||||
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
|
||||
.className(LauncherAppWidgetHostView.class).description(info.label);
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
|
||||
assertTrue("Widget is not present",
|
||||
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Convert test to TAPL; b/131116002
|
||||
public void testCustomProfileLoaded_with_folder() throws Exception {
|
||||
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
@@ -118,10 +108,7 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
mActivityMonitor.startLauncher();
|
||||
waitForModelLoaded();
|
||||
|
||||
// Verify widget present
|
||||
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
|
||||
.descriptionContains(mTargetContext.getString(android.R.string.copy));
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
|
||||
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.launcher3.tapl.TestHelpers;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
class PortraitLandscapeRunner implements TestRule {
|
||||
private AbstractLauncherUiTest mTest;
|
||||
|
||||
public PortraitLandscapeRunner(AbstractLauncherUiTest test) {
|
||||
mTest = test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
if (!TestHelpers.isInLauncherProcess() ||
|
||||
description.getAnnotation(AbstractLauncherUiTest.PortraitLandscape.class) == null) {
|
||||
return base;
|
||||
}
|
||||
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
mTest.mDevice.pressHome();
|
||||
mTest.waitForLauncherCondition("Launcher activity wasn't created",
|
||||
launcher -> launcher != null);
|
||||
|
||||
mTest.executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(
|
||||
true));
|
||||
|
||||
evaluateInPortrait();
|
||||
evaluateInLandscape();
|
||||
} finally {
|
||||
mTest.mDevice.setOrientationNatural();
|
||||
mTest.executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(
|
||||
false));
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateInPortrait() throws Throwable {
|
||||
mTest.mDevice.setOrientationNatural();
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
base.evaluate();
|
||||
mTest.getDevice().pressHome();
|
||||
}
|
||||
|
||||
private void evaluateInLandscape() throws Throwable {
|
||||
mTest.mDevice.setOrientationLeft();
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
|
||||
base.evaluate();
|
||||
mTest.getDevice().pressHome();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -59,11 +59,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
|
||||
public static void initialize(AbstractLauncherUiTest test) throws Exception {
|
||||
test.clearLauncherData();
|
||||
if (TestHelpers.isInLauncherProcess()) {
|
||||
test.mActivityMonitor.returnToHome();
|
||||
} else {
|
||||
test.mDevice.pressHome();
|
||||
}
|
||||
test.mDevice.pressHome();
|
||||
test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
|
||||
test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
|
||||
test.waitForResumed("Launcher internal state is still Background");
|
||||
@@ -279,8 +275,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testLaunchMenuItem() throws Exception {
|
||||
if (!TestHelpers.isInLauncherProcess()) return;
|
||||
|
||||
final AllApps allApps = mLauncher.
|
||||
getWorkspace().
|
||||
switchToAllApps();
|
||||
@@ -327,8 +321,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragShortcut() throws Throwable {
|
||||
if (!TestHelpers.isInLauncherProcess()) return;
|
||||
|
||||
// 1. Open all apps and wait for load complete.
|
||||
// 2. Find the app and long press it to show shortcuts.
|
||||
// 3. Press icon center until shortcuts appear
|
||||
|
||||
@@ -54,21 +54,6 @@ public class TestViewHelpers {
|
||||
return UiDevice.getInstance(getInstrumentation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens all apps and returns the recycler view
|
||||
*/
|
||||
public static UiObject2 openAllApps() {
|
||||
final UiDevice device = getDevice();
|
||||
device.waitForIdle();
|
||||
UiObject2 hotseat = device.wait(
|
||||
Until.findObject(getSelectorForId(R.id.hotseat)), 2500);
|
||||
Point start = hotseat.getVisibleCenter();
|
||||
int endY = (int) (device.getDisplayHeight() * 0.1f);
|
||||
// 100 px/step
|
||||
device.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100);
|
||||
return findViewById(R.id.apps_list_view);
|
||||
}
|
||||
|
||||
public static UiObject2 findViewById(int id) {
|
||||
return getDevice().wait(Until.findObject(getSelectorForId(id)),
|
||||
AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package com.android.launcher3.ui.widget;
|
||||
|
||||
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -24,43 +25,37 @@ import android.appwidget.AppWidgetHost;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.LauncherAppWidgetHost;
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||
import com.android.launcher3.compat.PackageInstallerCompat;
|
||||
import com.android.launcher3.tapl.Workspace;
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
import com.android.launcher3.ui.TestViewHelpers;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
import com.android.launcher3.util.rule.ShellCommandRule;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||
import com.android.launcher3.widget.PendingAppWidgetHostView;
|
||||
import com.android.launcher3.widget.WidgetHostViewLoader;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import androidx.test.uiautomator.UiSelector;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Tests for bind widget flow.
|
||||
*
|
||||
@@ -131,16 +126,14 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||
|
||||
setupContents(item);
|
||||
|
||||
// Since there is no widget to verify, just wait until the workspace is ready.
|
||||
// TODO: fix LauncherInstrumentation#LAUNCHER_PKG
|
||||
mLauncher.getWorkspace();
|
||||
final Workspace workspace = mLauncher.getWorkspace();
|
||||
// Item deleted from db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
assertEquals(0, mCursor.getCount());
|
||||
|
||||
// The view does not exist
|
||||
assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
|
||||
assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -189,12 +182,8 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||
|
||||
setupContents(item);
|
||||
|
||||
// Since there is no widget to verify, just wait until the workspace is ready.
|
||||
// TODO: fix LauncherInstrumentation#LAUNCHER_PKG
|
||||
mLauncher.getWorkspace();
|
||||
// The view does not exist
|
||||
assertFalse(mDevice.findObject(
|
||||
new UiSelector().className(PendingAppWidgetHostView.class)).exists());
|
||||
assertTrue("Pending widget exists",
|
||||
mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
|
||||
// Item deleted from db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
@@ -258,12 +247,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||
* widget class is displayed on the homescreen.
|
||||
*/
|
||||
private void setupContents(LauncherAppWidgetInfo item) {
|
||||
int screenId = Workspace.FIRST_SCREEN_ID;
|
||||
int screenId = FIRST_SCREEN_ID;
|
||||
// Update the screen id counter for the provider.
|
||||
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||
|
||||
if (screenId > Workspace.FIRST_SCREEN_ID) {
|
||||
screenId = Workspace.FIRST_SCREEN_ID;
|
||||
if (screenId > FIRST_SCREEN_ID) {
|
||||
screenId = FIRST_SCREEN_ID;
|
||||
}
|
||||
|
||||
// Insert the item
|
||||
@@ -283,15 +272,13 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||
}
|
||||
|
||||
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
|
||||
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
|
||||
.className(LauncherAppWidgetHostView.class).description(info.label);
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
|
||||
assertTrue("Widget is not present",
|
||||
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
private void verifyPendingWidgetPresent() {
|
||||
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
|
||||
.className(PendingAppWidgetHostView.class);
|
||||
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
|
||||
assertTrue("Pending widget is not present",
|
||||
mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.launcher3.ui.widget;
|
||||
|
||||
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
@@ -26,9 +28,6 @@ import android.view.View;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
@@ -37,15 +36,14 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace.ItemOperator;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.tapl.AddToHomeScreenPrompt;
|
||||
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
|
||||
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
|
||||
import com.android.launcher3.testcomponent.RequestPinItemActivity;
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
import com.android.launcher3.ui.TestViewHelpers;
|
||||
import com.android.launcher3.util.Condition;
|
||||
import com.android.launcher3.util.Wait;
|
||||
import com.android.launcher3.util.rule.ShellCommandRule;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -80,15 +78,10 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Test
|
||||
public void testPinWidgetNoConfig() throws Throwable {
|
||||
runTest("pinWidgetNoConfig", true, new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetNoConfig.class.getName());
|
||||
}
|
||||
});
|
||||
runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetNoConfig.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,28 +91,19 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
|
||||
RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
|
||||
|
||||
runTest("pinWidgetNoConfig", true, new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetNoConfig.class.getName());
|
||||
}
|
||||
}, command);
|
||||
runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetNoConfig.class.getName()), command);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPinWidgetWithConfig() throws Throwable {
|
||||
runTest("pinWidgetWithConfig", true, new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof LauncherAppWidgetInfo &&
|
||||
runTest("pinWidgetWithConfig", true,
|
||||
(info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetWithConfig.class.getName());
|
||||
}
|
||||
});
|
||||
.equals(AppWidgetWithConfig.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,14 +133,14 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
clearHomescreen();
|
||||
mActivityMonitor.startLauncher();
|
||||
|
||||
// Open all apps and wait for load complete
|
||||
final UiObject2 appsContainer = TestViewHelpers.openAllApps();
|
||||
Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
|
||||
|
||||
// Open Pin item activity
|
||||
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
|
||||
RequestPinItemActivity.class.getName());
|
||||
scrollAndFind(appsContainer, By.text("Test Pin Item")).click();
|
||||
mLauncher.
|
||||
getWorkspace().
|
||||
switchToAllApps().
|
||||
getAppIcon("Test Pin Item").
|
||||
launch(getAppPackageName());
|
||||
assertNotNull(openMonitor.blockingGetExtraIntent());
|
||||
|
||||
// Set callback
|
||||
@@ -173,15 +157,11 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
// call the requested method to start the flow
|
||||
mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
|
||||
RequestPinItemActivity.class, activityMethod));
|
||||
UiObject2 widgetCell = mDevice.wait(
|
||||
Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT);
|
||||
assertNotNull(widgetCell);
|
||||
final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt();
|
||||
|
||||
// Accept confirmation:
|
||||
BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
|
||||
mDevice.wait(Until.findObject(
|
||||
By.text(mLauncher.isAvd() ? "ADD AUTOMATICALLY" : "Add automatically")),
|
||||
DEFAULT_UI_TIMEOUT).click();
|
||||
addToHomeScreenPrompt.addAutomatically();
|
||||
Intent result = resultReceiver.blockingGetIntent();
|
||||
assertNotNull(result);
|
||||
mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
|
||||
@@ -190,7 +170,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
}
|
||||
|
||||
// Go back to home
|
||||
mActivityMonitor.returnToHome();
|
||||
mLauncher.pressHome();
|
||||
Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
import androidx.test.uiautomator.UiDevice;
|
||||
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
@@ -16,16 +16,16 @@ import java.io.IOException;
|
||||
public class FailureWatcher extends TestWatcher {
|
||||
private static final String TAG = "FailureWatcher";
|
||||
private static int sScreenshotCount = 0;
|
||||
private AbstractLauncherUiTest mAbstractLauncherUiTest;
|
||||
final private UiDevice mDevice;
|
||||
|
||||
public FailureWatcher(AbstractLauncherUiTest abstractLauncherUiTest) {
|
||||
mAbstractLauncherUiTest = abstractLauncherUiTest;
|
||||
public FailureWatcher(UiDevice device) {
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
private void dumpViewHierarchy() {
|
||||
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
try {
|
||||
mAbstractLauncherUiTest.getDevice().dumpWindowHierarchy(stream);
|
||||
mDevice.dumpWindowHierarchy(stream);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
for (String line : stream.toString().split("\\r?\\n")) {
|
||||
@@ -38,7 +38,7 @@ public class FailureWatcher extends TestWatcher {
|
||||
|
||||
@Override
|
||||
protected void failed(Throwable e, Description description) {
|
||||
if (mAbstractLauncherUiTest.getDevice() == null) return;
|
||||
if (mDevice == null) return;
|
||||
final String pathname = getInstrumentation().getTargetContext().
|
||||
getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
|
||||
Log.e(TAG, "Failed test " + description.getMethodName() +
|
||||
@@ -48,12 +48,12 @@ public class FailureWatcher extends TestWatcher {
|
||||
dumpViewHierarchy();
|
||||
|
||||
try {
|
||||
final String dumpsysResult = mAbstractLauncherUiTest.getDevice().executeShellCommand(
|
||||
final String dumpsysResult = mDevice.executeShellCommand(
|
||||
"dumpsys activity service TouchInteractionService");
|
||||
Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
|
||||
mAbstractLauncherUiTest.getDevice().takeScreenshot(new File(pathname));
|
||||
mDevice.takeScreenshot(new File(pathname));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +72,6 @@ public class LauncherActivityRule implements TestRule {
|
||||
getInstrumentation().startActivitySync(getHomeIntentInPackage(getTargetContext()));
|
||||
}
|
||||
|
||||
public void returnToHome() {
|
||||
getTargetContext().startActivity(getHomeIntentInPackage(getTargetContext()));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
private class MyStatement extends Statement implements ActivityLifecycleCallbacks {
|
||||
|
||||
private final Statement mBase;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
public class AddToHomeScreenPrompt {
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mWidgetCell;
|
||||
|
||||
AddToHomeScreenPrompt(LauncherInstrumentation launcher) {
|
||||
mLauncher = launcher;
|
||||
mWidgetCell = launcher.waitForLauncherObject(By.clazz(
|
||||
"com.android.launcher3.widget.WidgetCell"));
|
||||
mLauncher.assertNotNull("Can't find widget cell object", mWidgetCell);
|
||||
}
|
||||
|
||||
public void addAutomatically() {
|
||||
mLauncher.waitForObjectInContainer(
|
||||
mWidgetCell.getParent().getParent().getParent().getParent(),
|
||||
By.text(LauncherInstrumentation.isAvd()
|
||||
? "ADD AUTOMATICALLY"
|
||||
: "Add automatically")).
|
||||
click();
|
||||
}
|
||||
}
|
||||
@@ -87,16 +87,24 @@ public class Background extends LauncherInstrumentation.VisibleContainer {
|
||||
}
|
||||
|
||||
case TWO_BUTTON: {
|
||||
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
|
||||
final int startY = getSwipeStartY();
|
||||
final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
final int startX;
|
||||
final int startY;
|
||||
final int endX;
|
||||
final int endY;
|
||||
final int swipeLength = mLauncher.getTestInfo(getSwipeHeightRequestName()).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) + mLauncher.getTouchSlop();
|
||||
|
||||
mLauncher.swipeToState(
|
||||
centerX, startY, centerX,
|
||||
startY - swipeHeight - mLauncher.getTouchSlop(),
|
||||
10,
|
||||
expectedState);
|
||||
if (mLauncher.getDevice().isNaturalOrientation()) {
|
||||
startX = endX = mLauncher.getDevice().getDisplayWidth() / 2;
|
||||
startY = getSwipeStartY();
|
||||
endY = startY - swipeLength;
|
||||
} else {
|
||||
startX = getSwipeStartX();
|
||||
endX = startX - swipeLength;
|
||||
startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
|
||||
}
|
||||
|
||||
mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -111,6 +119,10 @@ public class Background extends LauncherInstrumentation.VisibleContainer {
|
||||
return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT;
|
||||
}
|
||||
|
||||
protected int getSwipeStartX() {
|
||||
return mLauncher.getRealDisplaySize().x - 1;
|
||||
}
|
||||
|
||||
protected int getSwipeStartY() {
|
||||
return mLauncher.getRealDisplaySize().y - 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
/**
|
||||
* App folder in workspace/
|
||||
*/
|
||||
public final class Folder {
|
||||
Folder(LauncherInstrumentation launcher, UiObject2 icon) {
|
||||
}
|
||||
|
||||
static BySelector getSelector(String folderName, LauncherInstrumentation launcher) {
|
||||
return By.clazz(FrameLayout.class).desc(folderName).pkg(launcher.getLauncherPackageName());
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public final class LauncherInstrumentation {
|
||||
private static final String APPS_RES_ID = "apps_view";
|
||||
private static final String OVERVIEW_RES_ID = "overview_panel";
|
||||
private static final String WIDGETS_RES_ID = "widgets_list_view";
|
||||
public static final int WAIT_TIME_MS = 10000;
|
||||
public static final int WAIT_TIME_MS = 60000;
|
||||
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
||||
|
||||
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
|
||||
@@ -315,25 +315,37 @@ public final class LauncherInstrumentation {
|
||||
mExpectedRotation = expectedRotation;
|
||||
}
|
||||
|
||||
private UiObject2 verifyContainerType(ContainerType containerType) {
|
||||
assertEquals("Unexpected display rotation",
|
||||
mExpectedRotation, mDevice.getDisplayRotation());
|
||||
public String getNavigationModeMismatchError() {
|
||||
final NavigationModel navigationModel = getNavigationModel();
|
||||
final boolean hasRecentsButton = hasSystemUiObject("recent_apps");
|
||||
final boolean hasHomeButton = hasSystemUiObject("home");
|
||||
assertTrue("Presence of recents button doesn't match the interaction mode, mode="
|
||||
+ navigationModel.name() + ", hasRecents=" + hasRecentsButton,
|
||||
(navigationModel == NavigationModel.THREE_BUTTON) == hasRecentsButton);
|
||||
assertTrue("Presence of home button doesn't match the interaction mode, mode="
|
||||
+ navigationModel.name() + ", hasHome=" + hasHomeButton,
|
||||
(navigationModel != NavigationModel.ZERO_BUTTON) == hasHomeButton);
|
||||
if ((navigationModel == NavigationModel.THREE_BUTTON) != hasRecentsButton) {
|
||||
return "Presence of recents button doesn't match the interaction mode, mode="
|
||||
+ navigationModel.name() + ", hasRecents=" + hasRecentsButton;
|
||||
}
|
||||
if ((navigationModel != NavigationModel.ZERO_BUTTON) != hasHomeButton) {
|
||||
return "Presence of home button doesn't match the interaction mode, mode="
|
||||
+ navigationModel.name() + ", hasHome=" + hasHomeButton;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private UiObject2 verifyContainerType(ContainerType containerType) {
|
||||
assertEquals("Unexpected display rotation",
|
||||
mExpectedRotation, mDevice.getDisplayRotation());
|
||||
final String error = getNavigationModeMismatchError();
|
||||
assertTrue(error, error == null);
|
||||
log("verifyContainerType: " + containerType);
|
||||
|
||||
try (Closable c = addContextLayer(
|
||||
"but the current state is not " + containerType.name())) {
|
||||
switch (containerType) {
|
||||
case WORKSPACE: {
|
||||
waitForLauncherObject(APPS_RES_ID);
|
||||
if (mDevice.isNaturalOrientation()) {
|
||||
waitForLauncherObject(APPS_RES_ID);
|
||||
} else {
|
||||
waitUntilGone(APPS_RES_ID);
|
||||
}
|
||||
waitUntilGone(OVERVIEW_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
return waitForLauncherObject(WORKSPACE_RES_ID);
|
||||
@@ -490,6 +502,13 @@ public final class LauncherInstrumentation {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AddToHomeScreenPrompt getAddToHomeScreenPrompt() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get widget cell")) {
|
||||
return new AddToHomeScreenPrompt(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Overview object if the current state is showing the overview panel. Fails if the
|
||||
* launcher is not in that state.
|
||||
@@ -503,17 +522,6 @@ public final class LauncherInstrumentation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Base overview object if either Launcher is in overview state or the fallback
|
||||
* overview activity is showing. Fails otherwise.
|
||||
*
|
||||
* @return BaseOverview object.
|
||||
*/
|
||||
@NonNull
|
||||
public BaseOverview getBaseOverview() {
|
||||
return new BaseOverview(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the All Apps object if the current state is showing the all apps panel opened by swiping
|
||||
* from workspace. Fails if the launcher is not in that state. Please don't call this method if
|
||||
@@ -604,6 +612,16 @@ public final class LauncherInstrumentation {
|
||||
return waitForObjectBySelector(getLauncherObjectSelector(resName));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForLauncherObject(BySelector selector) {
|
||||
return waitForObjectBySelector(selector.pkg(getLauncherPackageName()));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) {
|
||||
return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForFallbackLauncherObject(String resName) {
|
||||
return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
|
||||
@@ -615,6 +633,10 @@ public final class LauncherInstrumentation {
|
||||
return object;
|
||||
}
|
||||
|
||||
private UiObject2 tryWaitForObjectBySelector(BySelector selector, long timeout) {
|
||||
return mDevice.wait(Until.findObject(selector), timeout);
|
||||
}
|
||||
|
||||
BySelector getLauncherObjectSelector(String resName) {
|
||||
return By.res(getLauncherPackageName(), resName);
|
||||
}
|
||||
@@ -688,7 +710,7 @@ public final class LauncherInstrumentation {
|
||||
|
||||
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
|
||||
// fixed interval each time.
|
||||
private void linearGesture(int startX, int startY, int endX, int endY, int steps) {
|
||||
void linearGesture(int startX, int startY, int endX, int endY, int steps) {
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
final Point start = new Point(startX, startY);
|
||||
final Point end = new Point(endX, endY);
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
@@ -26,7 +27,6 @@ import com.android.launcher3.testing.TestProtocol;
|
||||
* A recent task in the overview panel carousel.
|
||||
*/
|
||||
public final class OverviewTask {
|
||||
static final int FLING_SPEED = 3000;
|
||||
private static final long WAIT_TIME_MS = 60000;
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mTask;
|
||||
@@ -51,7 +51,10 @@ public final class OverviewTask {
|
||||
"want to dismiss a task")) {
|
||||
verifyActiveContainer();
|
||||
// Dismiss the task via flinging it up.
|
||||
mTask.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
final Rect taskBounds = mTask.getVisibleBounds();
|
||||
final int centerX = taskBounds.centerX();
|
||||
final int centerY = taskBounds.centerY();
|
||||
mLauncher.linearGesture(centerX, centerY, centerX, 0, 10);
|
||||
mLauncher.waitForIdle();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-9
@@ -14,13 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3;
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
/**
|
||||
* Progress is defined as a value with range [0, 1], and is specific to each implementor.
|
||||
* It is used when there is a transition from one state of the UI to another.
|
||||
*/
|
||||
public interface ProgressInterface {
|
||||
void setProgress(float progress);
|
||||
float getProgress();
|
||||
}
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
public class Widget {
|
||||
Widget(LauncherInstrumentation launcher, UiObject2 widget) {
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
@@ -142,11 +143,17 @@ public final class Workspace extends Home {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private AppIcon getHotseatAppIcon(String appName) {
|
||||
public AppIcon getHotseatAppIcon(String appName) {
|
||||
return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
|
||||
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Folder getHotseatFolder(String appName) {
|
||||
return new Folder(mLauncher, mLauncher.getObjectInContainer(
|
||||
mHotseat, Folder.getSelector(appName, mLauncher)));
|
||||
}
|
||||
|
||||
static void dragIconToWorkspace(
|
||||
LauncherInstrumentation launcher, Launchable launchable, Point dest,
|
||||
String longPressIndicator) {
|
||||
@@ -213,6 +220,21 @@ public final class Workspace extends Home {
|
||||
|
||||
@Override
|
||||
protected int getSwipeStartY() {
|
||||
return mLauncher.waitForLauncherObject("hotseat").getVisibleBounds().top;
|
||||
return mLauncher.getRealDisplaySize().y - 1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Widget tryGetWidget(String label, long timeout) {
|
||||
final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
|
||||
By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView").desc(label),
|
||||
timeout);
|
||||
return widget != null ? new Widget(mLauncher, widget) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Widget tryGetPendingWidget(long timeout) {
|
||||
final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
|
||||
By.clazz("com.android.launcher3.widget.PendingAppWidgetHostView"), timeout);
|
||||
return widget != null ? new Widget(mLauncher, widget) : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user