Merging ub-launcher3-qt-qpr1-dev, build 5872416
Test: Manual Bug:121280703 P2 Finish implementation of PortraitLandscape annotation for tests Bug:135633159 P3 [QPR] Promise icons on home screen Bug:135638690 P1 [QPR] On-device app search Bug:137106918 P2 UX: Accidental NGA triggering when hitting Undo actions in apps Bug:137200188 P3 Home screen app title disappears during animation Bug:138195597 P2 Wrong icon animated on return to home screen Bug:138396187 P1 Swipe and hold from an app no longer uses spring to animate adjacent task Bug:138729157 P1 [Flaky test] java.lang.AssertionError: http://go/tapl : want to fling backwards in widgets, flung back, but the current state is not WIDGETS; Unexpected launcher object visible: workspace Bug:138729456 P1 [Flaky test] java.lang.AssertionError: http://go/tapl : Can't find an object with selector: BySelector [CLASS='\Qandroid.widget.TextView\E', PKG='\Qcom.google.android.apps.nexuslauncher\E', TEXT='\QShortcut 3\E'] (visible state: Workspace) Bug:138964490 P1 Support DeviceConfig to drive ToggleableFlags Bug:139021165 P2 [TEST TRACKER] [QPR] Promise icons on home screen Bug:139137636 P2 Create memory tests for Launcher Bug:139551306 P4 [Polish] Reduce shelf paddings in Overview Bug:139885365 P3 App open animation different between launcher suggested apps and launcher home screen/drawer Bug:139918680 P2 [a11y] Talkback shouldn't keep focusing on the background item and speak the description of the background item again after entering Widget list. Bug:140076379 P1 Launcher ub-launcher3-qt-qpr1-dev Branch Build Keeps Crashing due to Exception Bug:140252951 P2 Add widget launch test. Bug:140308849 P2 Jank during swipe up to home, especially noticeable after pausing first Bug:140311911 P2 Flake in Launcher tests: java.lang.AssertionError: Stable state != state: OverviewState, LauncherState Bug:140405990 P2 [a11y] Unable to add shortcut to Home screen by Voice access or Switch access. Bug:140819614 P1 If an install session is abandoned for an already installed app, the corresponding icon is removed Bug:140823188 P1 AppPredictionsUITests failing Bug:140837771 P1 Failing test: AddConfigWidgetTests and AddWidgetTests are failing Change-Id: I1efae6216c53b1fee3e105c9356ed43c4bf46c6e
This commit is contained in:
@@ -10,3 +10,6 @@ mrcasey@google.com
|
||||
sunnygoyal@google.com
|
||||
twickham@google.com
|
||||
winsonc@google.com
|
||||
|
||||
per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com
|
||||
per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com
|
||||
|
||||
@@ -32,6 +32,7 @@ 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.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -115,10 +116,10 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(Launcher launcher) {
|
||||
return getDefaultSwipeHeight(launcher.getDeviceProfile());
|
||||
return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(DeviceProfile dp) {
|
||||
public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
|
||||
return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
}
|
||||
|
||||
|
||||
+9
-12
@@ -18,16 +18,11 @@ 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;
|
||||
@@ -35,18 +30,17 @@ import android.animation.ObjectAnimator;
|
||||
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.launcher3.anim.SpringAnimationBuilder;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
|
||||
* {@link RecentsView}.
|
||||
@@ -156,8 +150,11 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
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);
|
||||
return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X)
|
||||
.setDampingRatio(0.8f)
|
||||
.setStiffness(250)
|
||||
.setValues(values)
|
||||
.build(mLauncher);
|
||||
default:
|
||||
return super.createStateElementAnimation(index, values);
|
||||
}
|
||||
|
||||
+11
-7
@@ -24,7 +24,6 @@ import android.app.prediction.AppPredictor;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
@@ -32,6 +31,8 @@ import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
|
||||
import com.android.launcher3.ItemInfoWithIcon;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.StateListener;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
|
||||
@@ -58,7 +59,7 @@ import java.util.List;
|
||||
* 4) Maintains the current active client id (for the predictions) and all updates are performed on
|
||||
* that client id.
|
||||
*/
|
||||
public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
|
||||
public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
|
||||
OnIDPChangeListener, OnUpdateListener {
|
||||
|
||||
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
|
||||
@@ -153,7 +154,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
||||
public void reapplyItemInfo(ItemInfoWithIcon info) { }
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
public void onStateTransitionStart(LauncherState toState) { }
|
||||
|
||||
@Override
|
||||
public void onStateTransitionComplete(LauncherState state) {
|
||||
if (mAppsView == null) {
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +166,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
||||
mPendingState = null;
|
||||
}
|
||||
if (mPendingState == null) {
|
||||
mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
Launcher.getLauncher(mAppsView.getContext()).getStateManager()
|
||||
.removeStateListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +175,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
||||
boolean registerListener = mPendingState == null;
|
||||
mPendingState = state;
|
||||
if (registerListener) {
|
||||
// OnGlobalLayoutListener is called whenever a view in the view tree changes
|
||||
// visibility. Add a listener and wait until appsView is invisible again.
|
||||
mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||
// Add a listener and wait until appsView is invisible again.
|
||||
Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -32,6 +32,7 @@ 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
@@ -159,11 +160,15 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(Launcher launcher) {
|
||||
return getDefaultSwipeHeight(launcher.getDeviceProfile());
|
||||
return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(DeviceProfile dp) {
|
||||
return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
|
||||
float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) {
|
||||
swipeHeight -= dp.getInsets().bottom;
|
||||
}
|
||||
return swipeHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-5
@@ -40,7 +40,6 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
@@ -58,7 +57,6 @@ 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.testing.TestProtocol;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
@@ -202,9 +200,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
// This ensures then the next swipe up to all-apps starts from scroll 0.
|
||||
activity.getAppsView().reset(false /* animate */);
|
||||
|
||||
// Optimization, hide the all apps view to prevent layout while initializing
|
||||
activity.getAppsView().getContentView().setVisibility(View.GONE);
|
||||
|
||||
return new AnimationFactory() {
|
||||
private ShelfAnimState mShelfState;
|
||||
private boolean mIsAttachedToWindow;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
switch (method) {
|
||||
case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
|
||||
final float swipeHeight =
|
||||
OverviewState.getDefaultSwipeHeight(mDeviceProfile);
|
||||
OverviewState.getDefaultSwipeHeight(mContext, mDeviceProfile);
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
|
||||
return response;
|
||||
}
|
||||
|
||||
+12
-7
@@ -1,3 +1,4 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
@@ -82,7 +83,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
private int mDirection;
|
||||
private ActivityControlHelper mActivityControlHelper;
|
||||
|
||||
private final float mDistThreshold;
|
||||
private final float mDragDistThreshold;
|
||||
private final float mFlingDistThreshold;
|
||||
private final long mTimeThreshold;
|
||||
private final int mAngleThreshold;
|
||||
private final float mSquaredSlop;
|
||||
@@ -97,7 +99,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
final Resources res = context.getResources();
|
||||
mContext = context;
|
||||
mSysUiProxy = systemUiProxy;
|
||||
mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
|
||||
mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
|
||||
mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
|
||||
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
|
||||
mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
|
||||
|
||||
@@ -117,8 +120,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
// TODO add logging
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case ACTION_DOWN: {
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
@@ -213,6 +214,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
break;
|
||||
}
|
||||
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
|
||||
if (mState != STATE_ACTIVE) {
|
||||
mDelegate.onMotionEvent(ev);
|
||||
}
|
||||
@@ -220,9 +223,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
|
||||
private void updateAssistantProgress() {
|
||||
if (!mLaunchedAssistant) {
|
||||
mLastProgress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
|
||||
mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction;
|
||||
try {
|
||||
if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
|
||||
if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
|
||||
mSysUiProxy.onAssistantGestureCompletion(0);
|
||||
startAssistantInternal(SWIPE);
|
||||
|
||||
@@ -271,7 +274,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
if (isValidAssistantGestureAngle(velocityX, -velocityY)
|
||||
&& !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) {
|
||||
&& mDistance >= mFlingDistThreshold
|
||||
&& !mLaunchedAssistant
|
||||
&& mState != STATE_DELEGATE_ACTIVE) {
|
||||
mLastProgress = 1;
|
||||
try {
|
||||
mSysUiProxy.onAssistantGestureCompletion(
|
||||
|
||||
+43
-12
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -40,7 +41,9 @@ import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -62,7 +65,9 @@ public class StaggeredWorkspaceAnim {
|
||||
|
||||
private final float mVelocity;
|
||||
private final float mSpringTransY;
|
||||
private final View mViewToIgnore;
|
||||
|
||||
// The original view of the {@link FloatingIconView}.
|
||||
private final View mOriginalView;
|
||||
|
||||
private final List<Animator> mAnimators = new ArrayList<>();
|
||||
|
||||
@@ -72,9 +77,7 @@ public class StaggeredWorkspaceAnim {
|
||||
public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
|
||||
float velocity) {
|
||||
mVelocity = velocity;
|
||||
// We ignore this view since it's visibility and position is controlled by
|
||||
// the FloatingIconView.
|
||||
mViewToIgnore = floatingViewOriginalView;
|
||||
mOriginalView = floatingViewOriginalView;
|
||||
|
||||
// Scale the translationY based on the initial velocity to better sync the workspace items
|
||||
// with the floating view.
|
||||
@@ -86,16 +89,21 @@ public class StaggeredWorkspaceAnim {
|
||||
Workspace workspace = launcher.getWorkspace();
|
||||
CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
|
||||
ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
|
||||
ViewGroup hotseat = launcher.getHotseat();
|
||||
|
||||
boolean workspaceClipChildren = workspace.getClipChildren();
|
||||
boolean workspaceClipToPadding = workspace.getClipToPadding();
|
||||
boolean cellLayoutClipChildren = cellLayout.getClipChildren();
|
||||
boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
|
||||
boolean hotseatClipChildren = hotseat.getClipChildren();
|
||||
boolean hotseatClipToPadding = hotseat.getClipToPadding();
|
||||
|
||||
workspace.setClipChildren(false);
|
||||
workspace.setClipToPadding(false);
|
||||
cellLayout.setClipChildren(false);
|
||||
cellLayout.setClipToPadding(false);
|
||||
hotseat.setClipChildren(false);
|
||||
hotseat.setClipToPadding(false);
|
||||
|
||||
// Hotseat and QSB takes up two additional rows.
|
||||
int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
|
||||
@@ -108,16 +116,18 @@ public class StaggeredWorkspaceAnim {
|
||||
}
|
||||
|
||||
// Set up springs for the hotseat and qsb.
|
||||
ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0);
|
||||
if (grid.isVerticalBarLayout()) {
|
||||
ViewGroup hotseat = (ViewGroup) launcher.getHotseat().getChildAt(0);
|
||||
for (int i = hotseat.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = hotseat.getChildAt(i);
|
||||
for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = hotseatChild.getChildAt(i);
|
||||
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
|
||||
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
|
||||
}
|
||||
} else {
|
||||
View hotseat = launcher.getHotseat().getChildAt(0);
|
||||
addStaggeredAnimationForView(hotseat, grid.inv.numRows + 1, totalRows);
|
||||
for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = hotseatChild.getChildAt(i);
|
||||
addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
|
||||
}
|
||||
|
||||
View qsb = launcher.findViewById(R.id.search_container_all_apps);
|
||||
addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
|
||||
@@ -140,6 +150,8 @@ public class StaggeredWorkspaceAnim {
|
||||
workspace.setClipToPadding(workspaceClipToPadding);
|
||||
cellLayout.setClipChildren(cellLayoutClipChildren);
|
||||
cellLayout.setClipToPadding(cellLayoutClipToPadding);
|
||||
hotseat.setClipChildren(hotseatClipChildren);
|
||||
hotseat.setClipToPadding(hotseatClipToPadding);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,16 +192,35 @@ public class StaggeredWorkspaceAnim {
|
||||
springTransY.setStartDelay(startDelay);
|
||||
mAnimators.add(springTransY);
|
||||
|
||||
if (v == mViewToIgnore) {
|
||||
return;
|
||||
ObjectAnimator alpha = getAlphaAnimator(v, startDelay);
|
||||
if (v == mOriginalView) {
|
||||
// For IconLabelDotViews, we just want the label to fade in.
|
||||
// Icon, badge, and dots will animate in separately (controlled via FloatingIconView)
|
||||
if (v instanceof IconLabelDotView) {
|
||||
alpha.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
IconLabelDotView view = (IconLabelDotView) v;
|
||||
view.setIconVisible(false);
|
||||
view.setForceHideDot(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
v.setAlpha(0);
|
||||
mAnimators.add(alpha);
|
||||
}
|
||||
|
||||
private ObjectAnimator getAlphaAnimator(View v, long startDelay) {
|
||||
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
|
||||
alpha.setInterpolator(LINEAR);
|
||||
alpha.setDuration(ALPHA_DURATION_MS);
|
||||
alpha.setStartDelay(startDelay);
|
||||
mAnimators.add(alpha);
|
||||
return alpha;
|
||||
|
||||
}
|
||||
|
||||
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<!-- 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>
|
||||
<dimen name="gestures_assistant_fling_threshold">55dp</dimen>
|
||||
|
||||
<!-- Distance to move elements when swiping up to go home from launcher -->
|
||||
<dimen name="home_pullback_distance">28dp</dimen>
|
||||
|
||||
@@ -16,17 +16,34 @@
|
||||
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.DeviceConfig;
|
||||
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
|
||||
|
||||
public class TogglableFlag extends BaseTogglableFlag {
|
||||
public static final String NAMESPACE_LAUNCHER = "launcher";
|
||||
public static final String TAG = "TogglableFlag";
|
||||
|
||||
public TogglableFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, defaultValue, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getInitialValue(boolean value) {
|
||||
return DeviceConfig.getBoolean("launcher", getKey(), value);
|
||||
public boolean getOverridenDefaultValue(boolean value) {
|
||||
return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChangeListener(Context context, Runnable r) {
|
||||
DeviceConfig.addOnPropertiesChangedListener(
|
||||
NAMESPACE_LAUNCHER,
|
||||
context.getMainExecutor(),
|
||||
(properties) -> {
|
||||
if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
|
||||
return;
|
||||
}
|
||||
initialize(context);
|
||||
r.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,12 +156,14 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
mDragHandleProgress = 1;
|
||||
mMidAlpha = 0;
|
||||
} else {
|
||||
mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
|
||||
Context context = getContext();
|
||||
mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
|
||||
mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
|
||||
Rect hotseatPadding = dp.getHotseatLayoutPadding();
|
||||
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
|
||||
- hotseatPadding.bottom - hotseatPadding.top;
|
||||
float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
|
||||
float dragHandleTop =
|
||||
Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp));
|
||||
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
|
||||
}
|
||||
mTopOffset = dp.getInsets().top - mShelfOffset;
|
||||
|
||||
@@ -69,10 +69,9 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
|
||||
private DigitalWellBeingToast getToast() {
|
||||
executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
|
||||
waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
|
||||
waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null);
|
||||
final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
|
||||
|
||||
return getFromLauncher(launcher -> {
|
||||
final TaskView task = getLatestTask(launcher);
|
||||
assertTrue("Latest task is not Calculator",
|
||||
CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
|
||||
return task.getDigitalWellBeingToast();
|
||||
|
||||
@@ -118,7 +118,6 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
assertTrue("Couldn't set overlay",
|
||||
setActiveOverlay(prevOverlayPkg, originalMode));
|
||||
}
|
||||
mLauncher.disableDebugTracing();
|
||||
}
|
||||
|
||||
private void evaluateWithThreeButtons() throws Throwable {
|
||||
|
||||
@@ -97,7 +97,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testOverview() throws Exception {
|
||||
mLauncher.enableDebugTracing();
|
||||
startTestApps();
|
||||
Overview overview = mLauncher.pressHome().switchToOverview();
|
||||
assertTrue("Launcher internal state didn't switch to Overview",
|
||||
@@ -177,7 +176,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
executeOnLauncher(
|
||||
launcher -> assertEquals("Still have tasks after dismissing all",
|
||||
0, getTaskCount(launcher)));
|
||||
mLauncher.disableDebugTracing();
|
||||
}
|
||||
|
||||
private int getCurrentOverviewPage(Launcher launcher) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -171,4 +172,13 @@ public abstract class BaseRecyclerView extends RecyclerView {
|
||||
* <p>Override in each subclass of this base class.
|
||||
*/
|
||||
public void onFastScrollCompleted() {}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(int state) {
|
||||
super.onScrollStateChanged(state);
|
||||
|
||||
if (state == SCROLL_STATE_IDLE) {
|
||||
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.util.TypedValue;
|
||||
import android.view.KeyEvent;
|
||||
@@ -54,8 +53,8 @@ import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.icons.IconCache.IconLoadRequest;
|
||||
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
|
||||
@@ -64,7 +63,8 @@ import java.text.NumberFormat;
|
||||
* because we want to make the bubble taller than the text and TextView's clip is
|
||||
* too aggressive.
|
||||
*/
|
||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
|
||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
|
||||
IconLabelDotView {
|
||||
|
||||
private static final int DISPLAY_WORKSPACE = 0;
|
||||
private static final int DISPLAY_ALL_APPS = 1;
|
||||
@@ -413,7 +413,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
}
|
||||
}
|
||||
|
||||
public void forceHideDot(boolean forceHideDot) {
|
||||
@Override
|
||||
public void setForceHideDot(boolean forceHideDot) {
|
||||
if (mForceHideDot == forceHideDot) {
|
||||
return;
|
||||
}
|
||||
@@ -602,6 +603,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIconVisible(boolean visible) {
|
||||
mIsIconVisible = visible;
|
||||
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
|
||||
|
||||
@@ -142,8 +142,11 @@ public class FastBitmapDrawable extends Drawable {
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mAlpha = alpha;
|
||||
mPaint.setAlpha(alpha);
|
||||
if (mAlpha != alpha) {
|
||||
mAlpha = alpha;
|
||||
mPaint.setAlpha(alpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -157,6 +157,7 @@ import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Default launcher application.
|
||||
@@ -209,9 +210,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
|
||||
|
||||
// How long to wait before the new-shortcut animation automatically pans the workspace
|
||||
private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
|
||||
@VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
|
||||
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
|
||||
@Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
|
||||
@Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
|
||||
|
||||
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
|
||||
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
|
||||
|
||||
@@ -94,6 +94,7 @@ public class LauncherAppState {
|
||||
if (FeatureFlags.IS_DOGFOOD_BUILD) {
|
||||
filter.addAction(ACTION_FORCE_ROLOAD);
|
||||
}
|
||||
FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
|
||||
|
||||
mContext.registerReceiver(mModel, filter);
|
||||
UserManagerCompat.getInstance(mContext).enableAndResetCache();
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
|
||||
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
@@ -51,6 +52,7 @@ import com.android.launcher3.model.UserLockStateChangedTask;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
@@ -211,6 +213,30 @@ public class LauncherModel extends BroadcastReceiver
|
||||
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
|
||||
}
|
||||
|
||||
public void onSessionFailure(String packageName, UserHandle user) {
|
||||
enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
|
||||
synchronized (dataModel) {
|
||||
for (ItemInfo info : dataModel.itemsIdMap) {
|
||||
if (info instanceof WorkspaceItemInfo
|
||||
&& ((WorkspaceItemInfo) info).hasPromiseIconUi()
|
||||
&& user.equals(info.user)
|
||||
&& info.getIntent() != null
|
||||
&& TextUtils.equals(packageName, info.getIntent().getPackage())) {
|
||||
removedIds.put(info.id, true /* remove */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!removedIds.isEmpty()) {
|
||||
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, UserHandle user) {
|
||||
onPackagesRemoved(user, packageName);
|
||||
|
||||
@@ -228,8 +228,9 @@ public class LauncherStateManager {
|
||||
private void goToState(LauncherState state, boolean animated, long delay,
|
||||
final Runnable onCompleteRunnable) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " + state + " @ " +
|
||||
Log.getStackTraceString(new Throwable()));
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " +
|
||||
state.getClass().getSimpleName() +
|
||||
" @ " + Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
animated &= Utilities.areAnimationsEnabled(mLauncher);
|
||||
if (mLauncher.isInState(state)) {
|
||||
@@ -411,6 +412,11 @@ public class LauncherStateManager {
|
||||
mState.onStateDisabled(mLauncher);
|
||||
}
|
||||
mState = state;
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.STABLE_STATE_MISMATCH, "onStateTransitionStart: " +
|
||||
state.getClass().getSimpleName() +
|
||||
" @ " + Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
mState.onStateEnabled(mLauncher);
|
||||
mLauncher.onStateSet(mState);
|
||||
|
||||
@@ -431,7 +437,9 @@ public class LauncherStateManager {
|
||||
mLastStableState = state.getHistoryForState(mCurrentStableState);
|
||||
mCurrentStableState = state;
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " + state);
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " +
|
||||
state.getClass().getSimpleName() +
|
||||
" @ " + Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.android.launcher3.accessibility;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
@@ -30,14 +32,14 @@ import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.PendingAddItemInfo;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.keyboard.CustomActionsPopup;
|
||||
import com.android.launcher3.notification.NotificationListener;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.ShortcutUtil;
|
||||
@@ -164,6 +166,13 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
public boolean performAction(final View host, final ItemInfo item, int action) {
|
||||
if (action == ACTION_LONG_CLICK && ShortcutUtil.isDeepShortcut(item)) {
|
||||
CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
|
||||
if (popup.canShow()) {
|
||||
popup.show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (action == MOVE) {
|
||||
beginAccessibleDrag(host, item);
|
||||
} else if (action == ADD_TO_WORKSPACE) {
|
||||
|
||||
@@ -26,13 +26,15 @@ import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.BaseRecyclerView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
@@ -41,8 +43,6 @@ import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* A RecyclerView with custom fast scroll support for the all apps view.
|
||||
*/
|
||||
@@ -114,6 +114,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
|
||||
if (mScrollbar != null) {
|
||||
mScrollbar.reattachThumbToScroll();
|
||||
}
|
||||
if (getLayoutManager() instanceof AppsGridLayoutManager) {
|
||||
AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
|
||||
if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
|
||||
// We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
|
||||
return;
|
||||
}
|
||||
}
|
||||
scrollToPosition(0);
|
||||
}
|
||||
|
||||
@@ -420,13 +427,4 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(int state) {
|
||||
super.onScrollStateChanged(state);
|
||||
|
||||
if (state == SCROLL_STATE_IDLE) {
|
||||
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ 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.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -32,7 +31,6 @@ import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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.anim;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.FloatProperty;
|
||||
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
/**
|
||||
* Utility class to build an object animator which follows the same path as a spring animation for
|
||||
* an underdamped spring.
|
||||
*/
|
||||
public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
||||
|
||||
private final T mTarget;
|
||||
private final FloatProperty<T> mProperty;
|
||||
|
||||
private float mStartValue;
|
||||
private float mEndValue;
|
||||
private float mVelocity = 0;
|
||||
|
||||
private float mStiffness = SpringForce.STIFFNESS_MEDIUM;
|
||||
private float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
private float mMinVisibleChange = 1;
|
||||
|
||||
// Multiplier to the min visible change value for value threshold
|
||||
private static final float THRESHOLD_MULTIPLIER = 0.65f;
|
||||
|
||||
/**
|
||||
* The spring equation is given as
|
||||
* x = e^(-beta*t/2) * (a cos(gamma * t) + b sin(gamma * t)
|
||||
* v = e^(-beta*t/2) * ((2 * a * gamma + beta * b) * sin(gamma * t)
|
||||
* + (a * beta - 2 * b * gamma) * cos(gamma * t)) / 2
|
||||
*
|
||||
* a = x(0)
|
||||
* b = beta * x(0) / (2 * gamma) + v(0) / gamma
|
||||
*/
|
||||
private double beta;
|
||||
private double gamma;
|
||||
|
||||
private double a, b;
|
||||
private double va, vb;
|
||||
|
||||
// Threshold for velocity and value to determine when it's reasonable to assume that the spring
|
||||
// is approximately at rest.
|
||||
private double mValueThreshold;
|
||||
private double mVelocityThreshold;
|
||||
|
||||
private float mCurrentTime = 0;
|
||||
|
||||
public SpringAnimationBuilder(T target, FloatProperty<T> property) {
|
||||
super("dynamic-spring-property");
|
||||
mTarget = target;
|
||||
mProperty = property;
|
||||
|
||||
mStartValue = mProperty.get(target);
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setEndValue(float value) {
|
||||
mEndValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStartValue(float value) {
|
||||
mStartValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setValues(float... values) {
|
||||
if (values.length > 1) {
|
||||
mStartValue = values[0];
|
||||
mEndValue = values[values.length - 1];
|
||||
} else {
|
||||
mEndValue = values[0];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStiffness(
|
||||
@FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
|
||||
if (stiffness <= 0) {
|
||||
throw new IllegalArgumentException("Spring stiffness constant must be positive.");
|
||||
}
|
||||
mStiffness = stiffness;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setDampingRatio(
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
|
||||
float dampingRatio) {
|
||||
if (dampingRatio <= 0 || dampingRatio >= 1) {
|
||||
throw new IllegalArgumentException("Damping ratio must be between 0 and 1");
|
||||
}
|
||||
mDampingRatio = dampingRatio;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setMinimumVisibleChange(
|
||||
@FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) {
|
||||
if (minimumVisibleChange <= 0) {
|
||||
throw new IllegalArgumentException("Minimum visible change must be positive.");
|
||||
}
|
||||
mMinVisibleChange = minimumVisibleChange;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStartVelocity(float startVelocity) {
|
||||
mVelocity = startVelocity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T object, float time) {
|
||||
mCurrentTime = time;
|
||||
mProperty.setValue(
|
||||
object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(T t) {
|
||||
return mCurrentTime;
|
||||
}
|
||||
|
||||
public ObjectAnimator build(Context context) {
|
||||
int singleFrameMs = DefaultDisplay.getSingleFrameMs(context);
|
||||
double naturalFreq = Math.sqrt(mStiffness);
|
||||
double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
|
||||
|
||||
// All the calculations assume the stable position to be 0, shift the values accordingly.
|
||||
beta = 2 * mDampingRatio * naturalFreq;
|
||||
gamma = dampedFreq;
|
||||
a = mStartValue - mEndValue;
|
||||
b = beta * a / (2 * gamma) + mVelocity / gamma;
|
||||
|
||||
va = a * beta / 2 - b * gamma;
|
||||
vb = a * gamma + beta * b / 2;
|
||||
|
||||
mValueThreshold = mMinVisibleChange * THRESHOLD_MULTIPLIER;
|
||||
|
||||
// This multiplier is used to calculate the velocity threshold given a certain value
|
||||
// threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
|
||||
// then the velocity is a reasonable threshold.
|
||||
mVelocityThreshold = mValueThreshold * 1000.0 / singleFrameMs;
|
||||
|
||||
// Find the duration (in seconds) for the spring to reach equilibrium.
|
||||
// equilibrium is reached when x = 0
|
||||
double duration = Math.atan2(-a, b) / gamma;
|
||||
|
||||
// Keep moving ahead until the velocity reaches equilibrium.
|
||||
double piByG = Math.PI / gamma;
|
||||
while (duration < 0 || Math.abs(exponentialComponent(duration) * cosSinV(duration))
|
||||
>= mVelocityThreshold) {
|
||||
duration += piByG;
|
||||
}
|
||||
|
||||
// Find the shortest time
|
||||
double edgeTime = Math.max(0, duration - piByG / 2);
|
||||
double minDiff = singleFrameMs / 2000.0; // Half frame time in seconds
|
||||
|
||||
do {
|
||||
if ((duration - edgeTime) < minDiff) {
|
||||
break;
|
||||
}
|
||||
double mid = (edgeTime + duration) / 2;
|
||||
if (isAtEquilibrium(mid)) {
|
||||
duration = mid;
|
||||
} else {
|
||||
edgeTime = mid;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
|
||||
long durationMs = (long) (1000.0 * duration);
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
|
||||
animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
|
||||
animator.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mProperty.setValue(mTarget, mEndValue);
|
||||
}
|
||||
});
|
||||
return animator;
|
||||
}
|
||||
|
||||
private boolean isAtEquilibrium(double t) {
|
||||
double ec = exponentialComponent(t);
|
||||
|
||||
if (Math.abs(ec * cosSinX(t)) >= mValueThreshold) {
|
||||
return false;
|
||||
}
|
||||
return Math.abs(ec * cosSinV(t)) < mVelocityThreshold;
|
||||
}
|
||||
|
||||
private double exponentialComponent(double t) {
|
||||
return Math.pow(Math.E, - beta * t / 2);
|
||||
}
|
||||
|
||||
private double cosSinX(double t) {
|
||||
return cosSin(t, a, b);
|
||||
}
|
||||
|
||||
private double cosSinV(double t) {
|
||||
return cosSin(t, va, vb);
|
||||
}
|
||||
|
||||
private double cosSin(double t, double cosFactor, double sinFactor) {
|
||||
double angle = t * gamma;
|
||||
return cosFactor * Math.cos(angle) + sinFactor * Math.sin(angle);
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,30 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a promise app icon to the workspace iff:
|
||||
* - The settings for it are enabled
|
||||
* - The user installed the app
|
||||
* - There is an app icon and label (For apps with no launching activity, no icon is provided).
|
||||
* - The app is not already installed
|
||||
* - A promise icon for the session has not already been created
|
||||
*/
|
||||
private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
|
||||
if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
|
||||
&& SessionCommitReceiver.isEnabled(mAppContext)
|
||||
&& verify(sessionInfo) != null
|
||||
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
|
||||
&& sessionInfo.getAppIcon() != null
|
||||
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
|
||||
&& !mPromiseIconIds.contains(sessionInfo.getSessionId())
|
||||
&& mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
|
||||
getUserHandle(sessionInfo)) == null) {
|
||||
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
|
||||
mPromiseIconIds.add(sessionInfo.getSessionId());
|
||||
updatePromiseIconPrefs();
|
||||
}
|
||||
}
|
||||
|
||||
private final SessionCallback mCallback = new SessionCallback() {
|
||||
|
||||
@Override
|
||||
@@ -149,16 +173,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
}
|
||||
}
|
||||
|
||||
if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
|
||||
&& SessionCommitReceiver.isEnabled(mAppContext)
|
||||
&& sessionInfo != null
|
||||
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER) {
|
||||
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
|
||||
if (!mPromiseIconIds.contains(sessionInfo.getSessionId())) {
|
||||
mPromiseIconIds.add(sessionInfo.getSessionId());
|
||||
updatePromiseIconPrefs();
|
||||
}
|
||||
}
|
||||
tryQueuePromiseAppIcon(sessionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,12 +188,14 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
|
||||
packageName, key.mUser));
|
||||
|
||||
if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
|
||||
if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
|
||||
&& mPromiseIconIds.contains(sessionId)) {
|
||||
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
|
||||
if (appState != null) {
|
||||
LauncherModel model = appState.getModel();
|
||||
model.onPackageRemoved(packageName, key.mUser);
|
||||
appState.getModel().onSessionFailure(packageName, key.mUser);
|
||||
}
|
||||
// If it is successful, the id is removed in the the package added flow.
|
||||
removePromiseIconId(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,7 +213,10 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
|
||||
@Override
|
||||
public void onBadgingChanged(int sessionId) {
|
||||
pushSessionDisplayToLauncher(sessionId);
|
||||
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
|
||||
if (sessionInfo != null) {
|
||||
tryQueuePromiseAppIcon(sessionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
|
||||
|
||||
@@ -143,6 +143,8 @@ public abstract class BaseFlags {
|
||||
|
||||
public static abstract class BaseTogglableFlag {
|
||||
private final String key;
|
||||
// should be value that is hardcoded in client side.
|
||||
// Comparatively, getDefaultValue() can be overridden.
|
||||
private final boolean defaultValue;
|
||||
private final String description;
|
||||
private boolean currentValue;
|
||||
@@ -152,8 +154,9 @@ public abstract class BaseFlags {
|
||||
boolean defaultValue,
|
||||
String description) {
|
||||
this.key = checkNotNull(key);
|
||||
this.currentValue = this.defaultValue = getInitialValue(defaultValue);
|
||||
this.currentValue = this.defaultValue = defaultValue;
|
||||
this.description = checkNotNull(description);
|
||||
|
||||
synchronized (sLock) {
|
||||
sFlags.add((TogglableFlag)this);
|
||||
}
|
||||
@@ -169,16 +172,18 @@ public abstract class BaseFlags {
|
||||
return key;
|
||||
}
|
||||
|
||||
void initialize(Context context) {
|
||||
currentValue = getFromStorage(context, defaultValue);
|
||||
protected void initialize(Context context) {
|
||||
currentValue = getFromStorage(context, getDefaultValue());
|
||||
}
|
||||
|
||||
protected abstract boolean getInitialValue(boolean value);
|
||||
protected abstract boolean getOverridenDefaultValue(boolean value);
|
||||
|
||||
protected abstract void addChangeListener(Context context, Runnable r);
|
||||
|
||||
public void updateStorage(Context context, boolean value) {
|
||||
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
|
||||
Context.MODE_PRIVATE).edit();
|
||||
if (value == defaultValue) {
|
||||
if (value == getDefaultValue()) {
|
||||
editor.remove(key).apply();
|
||||
} else {
|
||||
editor.putBoolean(key, value).apply();
|
||||
@@ -187,11 +192,11 @@ public abstract class BaseFlags {
|
||||
|
||||
boolean getFromStorage(Context context, boolean defaultValue) {
|
||||
return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(key, defaultValue);
|
||||
.getBoolean(key, getDefaultValue());
|
||||
}
|
||||
|
||||
boolean getDefaultValue() {
|
||||
return defaultValue;
|
||||
return getOverridenDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
/** Returns the value of the flag at process start, including any overrides present. */
|
||||
@@ -208,6 +213,8 @@ public abstract class BaseFlags {
|
||||
return "TogglableFlag{"
|
||||
+ "key=" + key + ", "
|
||||
+ "defaultValue=" + defaultValue + ", "
|
||||
+ "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
|
||||
+ "currentValue=" + currentValue + ", "
|
||||
+ "description=" + description
|
||||
+ "}";
|
||||
}
|
||||
@@ -220,7 +227,7 @@ public abstract class BaseFlags {
|
||||
if (o instanceof TogglableFlag) {
|
||||
BaseTogglableFlag that = (BaseTogglableFlag) o;
|
||||
return (this.key.equals(that.getKey()))
|
||||
&& (this.defaultValue == that.getDefaultValue())
|
||||
&& (this.getDefaultValue() == that.getDefaultValue())
|
||||
&& (this.description.equals(that.getDescription()));
|
||||
}
|
||||
return false;
|
||||
@@ -232,7 +239,7 @@ public abstract class BaseFlags {
|
||||
h$ *= 1000003;
|
||||
h$ ^= key.hashCode();
|
||||
h$ *= 1000003;
|
||||
h$ ^= defaultValue ? 1231 : 1237;
|
||||
h$ ^= getDefaultValue() ? 1231 : 1237;
|
||||
h$ *= 1000003;
|
||||
h$ ^= description.hashCode();
|
||||
return h$;
|
||||
|
||||
@@ -61,6 +61,9 @@ public abstract class DragDriver {
|
||||
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_CANCEL");
|
||||
}
|
||||
mEventListener.onDriverDragCancel();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mFolderIcon.setBackgroundVisible(false);
|
||||
mFolderIcon.setIconVisible(false);
|
||||
mFolderIcon.drawLeaveBehindIfExists();
|
||||
}
|
||||
@Override
|
||||
@@ -646,7 +646,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
clearFocus();
|
||||
if (mFolderIcon != null) {
|
||||
mFolderIcon.setVisibility(View.VISIBLE);
|
||||
mFolderIcon.setBackgroundVisible(true);
|
||||
mFolderIcon.setIconVisible(true);
|
||||
mFolderIcon.mFolderName.setTextVisibility(true);
|
||||
if (wasAnimated) {
|
||||
mFolderIcon.animateBgShadowAndStroke();
|
||||
|
||||
@@ -65,6 +65,7 @@ import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -73,7 +74,7 @@ import java.util.List;
|
||||
/**
|
||||
* An icon that can appear on in the workspace representing an {@link Folder}.
|
||||
*/
|
||||
public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
|
||||
|
||||
@Thunk Launcher mLauncher;
|
||||
@Thunk Folder mFolder;
|
||||
@@ -107,6 +108,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
|
||||
private Alarm mOpenAlarm = new Alarm();
|
||||
|
||||
private boolean mForceHideDot;
|
||||
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
|
||||
private FolderDotInfo mDotInfo = new FolderDotInfo();
|
||||
private DotRenderer mDotRenderer;
|
||||
@@ -409,6 +411,20 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
return mPreviewLayoutRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceHideDot(boolean forceHideDot) {
|
||||
if (mForceHideDot == forceHideDot) {
|
||||
return;
|
||||
}
|
||||
mForceHideDot = forceHideDot;
|
||||
|
||||
if (forceHideDot) {
|
||||
invalidate();
|
||||
} else if (hasDot()) {
|
||||
animateDotScale(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false
|
||||
* (the dot is being added or removed).
|
||||
@@ -468,7 +484,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
mBackground.setInvalidateDelegate(this);
|
||||
}
|
||||
|
||||
public void setBackgroundVisible(boolean visible) {
|
||||
@Override
|
||||
public void setIconVisible(boolean visible) {
|
||||
mBackgroundIsVisible = visible;
|
||||
invalidate();
|
||||
}
|
||||
@@ -509,7 +526,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
}
|
||||
|
||||
public void drawDot(Canvas canvas) {
|
||||
if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
|
||||
if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
|
||||
Rect iconBounds = mDotParams.iconBounds;
|
||||
BubbleTextView.getIconBounds(this, iconBounds,
|
||||
mLauncher.getWallpaperDeviceProfile().iconSizePx);
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.UserManagerCompat;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
|
||||
import com.android.launcher3.icons.cache.BaseIconCache;
|
||||
import com.android.launcher3.icons.cache.CachingLogic;
|
||||
@@ -237,7 +238,8 @@ public class IconCache extends BaseIconCache {
|
||||
|
||||
@Override
|
||||
protected String getIconSystemState(String packageName) {
|
||||
return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
|
||||
return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
|
||||
+ ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
|
||||
}
|
||||
|
||||
public static abstract class IconLoadRequest extends HandlerRunnable {
|
||||
|
||||
@@ -36,7 +36,6 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -53,7 +52,6 @@ import com.android.launcher3.ItemInfoWithIcon;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
@@ -65,10 +63,8 @@ import com.android.launcher3.notification.NotificationInfo;
|
||||
import com.android.launcher3.notification.NotificationItemView;
|
||||
import com.android.launcher3.notification.NotificationKeyData;
|
||||
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
@@ -301,7 +297,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
|
||||
}
|
||||
|
||||
mLauncher.getDragController().addDragListener(this);
|
||||
mOriginalIcon.forceHideDot(true);
|
||||
mOriginalIcon.setForceHideDot(true);
|
||||
|
||||
// All views are added. Animate layout from now on.
|
||||
setLayoutTransition(new LayoutTransition());
|
||||
@@ -564,14 +560,14 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
|
||||
protected void onCreateCloseAnimation(AnimatorSet anim) {
|
||||
// Animate original icon's text back in.
|
||||
anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
|
||||
mOriginalIcon.forceHideDot(false);
|
||||
mOriginalIcon.setForceHideDot(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeComplete() {
|
||||
super.closeComplete();
|
||||
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
|
||||
mOriginalIcon.forceHideDot(false);
|
||||
mOriginalIcon.setForceHideDot(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -173,12 +173,6 @@ public class RestoreDbTask {
|
||||
values.put(Favorites.PROFILE_ID, newProfileId);
|
||||
db.update(Favorites.TABLE_NAME, values, "profileId = ?",
|
||||
new String[]{Long.toString(oldProfileId)});
|
||||
|
||||
// Change default value of the column.
|
||||
db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
|
||||
Favorites.addTableToDb(db, newProfileId, false);
|
||||
db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
|
||||
dropTable(db, "favorites_old");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.launcher3.testing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
@@ -113,13 +114,13 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_ALLOCATED_MEMORY: {
|
||||
final Runtime runtime = Runtime.getRuntime();
|
||||
response.putLong(TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
||||
runtime.totalMemory() - runtime.freeMemory());
|
||||
case TestProtocol.REQUEST_TOTAL_PSS_KB: {
|
||||
Debug.MemoryInfo mem = new Debug.MemoryInfo();
|
||||
Debug.getMemoryInfo(mem);
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public final class TestProtocol {
|
||||
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
|
||||
public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
|
||||
public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
|
||||
public static final String REQUEST_ALLOCATED_MEMORY = "allocated-memory";
|
||||
public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
|
||||
|
||||
public static boolean sDebugTracing = false;
|
||||
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
|
||||
@@ -83,4 +83,5 @@ public final class TestProtocol {
|
||||
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
|
||||
public static final String APP_NOT_DISABLED = "b/139891609";
|
||||
public static final String ALL_APPS_UPON_RECENTS = "b/139941530";
|
||||
public static final String STABLE_STATE_MISMATCH = "b/140311911";
|
||||
}
|
||||
|
||||
@@ -23,37 +23,57 @@ import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
|
||||
public class ShortcutUtil {
|
||||
public static boolean supportsShortcuts(ItemInfo info) {
|
||||
return isActive(info) && (isApp(info) || isPinnedShortcut(info));
|
||||
}
|
||||
/**
|
||||
* Returns true when we should show shortcut menu for the item.
|
||||
*/
|
||||
public static boolean supportsShortcuts(ItemInfo info) {
|
||||
return isActive(info) && (isApp(info) || isPinnedShortcut(info));
|
||||
}
|
||||
|
||||
public static boolean supportsDeepShortcuts(ItemInfo info) {
|
||||
return isActive(info) && isApp(info);
|
||||
}
|
||||
/**
|
||||
* Returns true when we should show depp shortcuts in shortcut menu for the item.
|
||||
*/
|
||||
public static boolean supportsDeepShortcuts(ItemInfo info) {
|
||||
return isActive(info) && isApp(info);
|
||||
}
|
||||
|
||||
public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
|
||||
return isActive(info) && isPinnedShortcut(info) ?
|
||||
ShortcutKey.fromItemInfo(info).getId() : null;
|
||||
}
|
||||
/**
|
||||
* Returns the shortcut id if the item is a pinned shortcut.
|
||||
*/
|
||||
public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
|
||||
return isActive(info) && isPinnedShortcut(info)
|
||||
? ShortcutKey.fromItemInfo(info).getId() : null;
|
||||
}
|
||||
|
||||
public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
|
||||
return isActive(info) && isPinnedShortcut(info) ?
|
||||
((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
/**
|
||||
* Returns the person keys associated with the item. (Has no function right now.)
|
||||
*/
|
||||
public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
|
||||
return isActive(info) && isPinnedShortcut(info)
|
||||
? ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
private static boolean isActive(ItemInfo info) {
|
||||
boolean isLoading = info instanceof WorkspaceItemInfo
|
||||
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();
|
||||
return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
|
||||
}
|
||||
/**
|
||||
* Returns true if the item is a deep shortcut.
|
||||
*/
|
||||
public static boolean isDeepShortcut(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& info instanceof WorkspaceItemInfo;
|
||||
}
|
||||
|
||||
private static boolean isApp(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
}
|
||||
private static boolean isActive(ItemInfo info) {
|
||||
boolean isLoading = info instanceof WorkspaceItemInfo
|
||||
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();
|
||||
return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
|
||||
}
|
||||
|
||||
private static boolean isPinnedShortcut(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& info.container != ItemInfo.NO_ID
|
||||
&& info instanceof WorkspaceItemInfo;
|
||||
}
|
||||
private static boolean isApp(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
}
|
||||
|
||||
private static boolean isPinnedShortcut(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& info.container != ItemInfo.NO_ID
|
||||
&& info instanceof WorkspaceItemInfo;
|
||||
}
|
||||
}
|
||||
@@ -153,15 +153,15 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
||||
}
|
||||
|
||||
protected void handleClose(boolean animate, long defaultDuration) {
|
||||
if (mIsOpen && !animate) {
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
if (!animate) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
setTranslationShift(TRANSLATION_SHIFT_CLOSED);
|
||||
onCloseComplete();
|
||||
return;
|
||||
}
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
mOpenCloseAnimator.setValues(
|
||||
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
|
||||
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -41,6 +42,7 @@ import android.widget.FrameLayout;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.InsettableFrameLayout;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
@@ -261,6 +263,10 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
}
|
||||
case ACTION_CANCEL:
|
||||
case ACTION_UP:
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE,
|
||||
"BaseDragLayer.ACTION_UP/CANCEL " + ev);
|
||||
}
|
||||
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
|
||||
mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
|
||||
break;
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.launcher3.views;
|
||||
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
|
||||
import static com.android.launcher3.Utilities.getBadge;
|
||||
import static com.android.launcher3.Utilities.getFullDrawable;
|
||||
import static com.android.launcher3.Utilities.isRtl;
|
||||
import static com.android.launcher3.Utilities.mapToRange;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
|
||||
@@ -564,11 +563,6 @@ public class FloatingIconView extends View implements
|
||||
*/
|
||||
private void checkIconResult(View originalView, boolean isOpening) {
|
||||
CancellationSignal cancellationSignal = new CancellationSignal();
|
||||
if (!isOpening) {
|
||||
// Hide immediately since the floating view starts at a different location.
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
cancellationSignal.setOnCancelListener(() -> originalView.setVisibility(VISIBLE));
|
||||
}
|
||||
|
||||
if (mIconLoadResult == null) {
|
||||
Log.w(TAG, "No icon load result found in checkIconResult");
|
||||
@@ -580,7 +574,7 @@ public class FloatingIconView extends View implements
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
if (isOpening) {
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
hideOriginalView(originalView);
|
||||
}
|
||||
} else {
|
||||
mIconLoadResult.onIconLoaded = () -> {
|
||||
@@ -591,15 +585,26 @@ public class FloatingIconView extends View implements
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
|
||||
// Delay swapping views until the icon is loaded to prevent a flash.
|
||||
setVisibility(VISIBLE);
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
if (isOpening) {
|
||||
// Delay swapping views until the icon is loaded to prevent a flash.
|
||||
hideOriginalView(originalView);
|
||||
}
|
||||
};
|
||||
mLoadIconSignal = cancellationSignal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hideOriginalView(View originalView) {
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
((BubbleTextView) originalView).setIconVisible(false);
|
||||
((BubbleTextView) originalView).setForceHideDot(true);
|
||||
} else {
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackgroundDrawableBounds(float scale) {
|
||||
sTmpRect.set(mFinalDrawableBounds);
|
||||
Utilities.scaleRectAboutCenter(sTmpRect, scale);
|
||||
@@ -716,7 +721,7 @@ public class FloatingIconView extends View implements
|
||||
*/
|
||||
@UiThread
|
||||
public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
|
||||
IconLoadResult result = new IconLoadResult();
|
||||
IconLoadResult result = new IconLoadResult(info);
|
||||
new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
|
||||
RectF position = new RectF();
|
||||
getLocationBoundsForView(l, v, isOpening, position);
|
||||
@@ -745,10 +750,13 @@ public class FloatingIconView extends View implements
|
||||
|
||||
// Get the drawable on the background thread
|
||||
boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
|
||||
view.mIconLoadResult = sIconLoadResult;
|
||||
if (shouldLoadIcon && view.mIconLoadResult == null) {
|
||||
view.mIconLoadResult = fetchIcon(launcher, originalView,
|
||||
(ItemInfo) originalView.getTag(), isOpening);
|
||||
if (shouldLoadIcon) {
|
||||
if (sIconLoadResult != null && sIconLoadResult.itemInfo == originalView.getTag()) {
|
||||
view.mIconLoadResult = sIconLoadResult;
|
||||
} else {
|
||||
view.mIconLoadResult = fetchIcon(launcher, originalView,
|
||||
(ItemInfo) originalView.getTag(), isOpening);
|
||||
}
|
||||
}
|
||||
sIconLoadResult = null;
|
||||
|
||||
@@ -776,7 +784,12 @@ public class FloatingIconView extends View implements
|
||||
|
||||
if (hideOriginal) {
|
||||
if (isOpening) {
|
||||
originalView.setVisibility(VISIBLE);
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
((BubbleTextView) originalView).setIconVisible(true);
|
||||
((BubbleTextView) originalView).setForceHideDot(false);
|
||||
} else {
|
||||
originalView.setVisibility(VISIBLE);
|
||||
}
|
||||
view.finish(dragLayer);
|
||||
} else {
|
||||
view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
|
||||
@@ -804,38 +817,33 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
});
|
||||
|
||||
if (mBadge != null && !(mOriginalIcon instanceof FolderIcon)) {
|
||||
if (mBadge != null) {
|
||||
ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
|
||||
badgeFade.addUpdateListener(valueAnimator -> invalidate());
|
||||
fade.play(badgeFade);
|
||||
}
|
||||
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
BubbleTextView btv = (BubbleTextView) originalView;
|
||||
btv.forceHideDot(true);
|
||||
if (originalView instanceof IconLabelDotView) {
|
||||
IconLabelDotView view = (IconLabelDotView) originalView;
|
||||
fade.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
btv.forceHideDot(false);
|
||||
view.setIconVisible(true);
|
||||
view.setForceHideDot(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (originalView instanceof FolderIcon) {
|
||||
FolderIcon folderIcon = (FolderIcon) originalView;
|
||||
folderIcon.setBackgroundVisible(false);
|
||||
folderIcon.getFolderName().setTextVisibility(false);
|
||||
fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
BubbleTextView btv = (BubbleTextView) originalView;
|
||||
fade.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
folderIcon.setBackgroundVisible(true);
|
||||
if (folderIcon.hasDot()) {
|
||||
folderIcon.animateDotScale(0, 1f);
|
||||
}
|
||||
public void onAnimationStart(Animator animation) {
|
||||
btv.setIconVisible(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
|
||||
} else if (!(originalView instanceof FolderIcon)) {
|
||||
fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
|
||||
}
|
||||
|
||||
@@ -890,10 +898,15 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
|
||||
private static class IconLoadResult {
|
||||
final ItemInfo itemInfo;
|
||||
Drawable drawable;
|
||||
Drawable badge;
|
||||
int iconOffset;
|
||||
Runnable onIconLoaded;
|
||||
boolean isIconLoaded;
|
||||
|
||||
public IconLoadResult(ItemInfo itemInfo) {
|
||||
this.itemInfo = itemInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.views;
|
||||
|
||||
/**
|
||||
* A view that has an icon, label, and notification dot.
|
||||
*/
|
||||
public interface IconLabelDotView {
|
||||
void setIconVisible(boolean visible);
|
||||
void setForceHideDot(boolean hide);
|
||||
}
|
||||
@@ -18,10 +18,8 @@ package com.android.launcher3.views;
|
||||
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
|
||||
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.text.TextUtils;
|
||||
@@ -33,6 +31,9 @@ import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -46,7 +47,6 @@ import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Popup shown on long pressing an empty space in launcher
|
||||
@@ -169,16 +169,17 @@ public class OptionsPopupView extends ArrowPopup
|
||||
}
|
||||
|
||||
public static boolean onWidgetsClicked(View view) {
|
||||
return openWidgets(Launcher.getLauncher(view.getContext()));
|
||||
return openWidgets(Launcher.getLauncher(view.getContext())) != null;
|
||||
}
|
||||
|
||||
public static boolean openWidgets(Launcher launcher) {
|
||||
/** Returns WidgetsFullSheet that was opened, or null if nothing was opened. */
|
||||
@Nullable
|
||||
public static WidgetsFullSheet openWidgets(Launcher launcher) {
|
||||
if (launcher.getPackageManager().isSafeMode()) {
|
||||
Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
return null;
|
||||
} else {
|
||||
WidgetsFullSheet.show(launcher, true /* animated */);
|
||||
return true;
|
||||
return WidgetsFullSheet.show(launcher, true /* animated */);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ package com.android.launcher3.views;
|
||||
import static android.content.Context.ACCESSIBILITY_SERVICE;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
|
||||
import static androidx.core.graphics.ColorUtils.compositeColors;
|
||||
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
||||
|
||||
import static androidx.core.graphics.ColorUtils.compositeColors;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.Keyframe;
|
||||
@@ -47,6 +47,13 @@ import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
|
||||
import androidx.customview.widget.ExploreByTouchHelper;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -62,15 +69,10 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
|
||||
import androidx.customview.widget.ExploreByTouchHelper;
|
||||
|
||||
/**
|
||||
* Simple scrim which draws a flat color
|
||||
@@ -325,7 +327,7 @@ public class ScrimView extends View implements Insettable, OnChangeListener,
|
||||
|
||||
if (enabled) {
|
||||
stateManager.addStateListener(this);
|
||||
handleStateChangedComplete(mLauncher.getStateManager().getState());
|
||||
handleStateChangedComplete(stateManager.getState());
|
||||
} else {
|
||||
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
@@ -437,7 +439,24 @@ public class ScrimView extends View implements Insettable, OnChangeListener,
|
||||
} else if (action == WALLPAPERS) {
|
||||
return OptionsPopupView.startWallpaperPicker(ScrimView.this);
|
||||
} else if (action == WIDGETS) {
|
||||
return OptionsPopupView.onWidgetsClicked(ScrimView.this);
|
||||
int originalImportanceForAccessibility = getImportantForAccessibility();
|
||||
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
|
||||
if (widgetsFullSheet == null) {
|
||||
setImportantForAccessibility(originalImportanceForAccessibility);
|
||||
return false;
|
||||
}
|
||||
widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View view) {}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View view) {
|
||||
setImportantForAccessibility(originalImportanceForAccessibility);
|
||||
widgetsFullSheet.removeOnAttachStateChangeListener(this);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if (action == SETTINGS) {
|
||||
return OptionsPopupView.startSettings(ScrimView.this);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
import android.content.Context;
|
||||
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
|
||||
|
||||
public class TogglableFlag extends BaseTogglableFlag {
|
||||
@@ -25,7 +26,10 @@ public class TogglableFlag extends BaseTogglableFlag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getInitialValue(boolean value) {
|
||||
public boolean getOverridenDefaultValue(boolean value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChangeListener(Context context, Runnable r) { }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.compat;
|
||||
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
/**
|
||||
* Test to verify promise icon flow.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PromiseIconUiTest extends AbstractLauncherUiTest {
|
||||
|
||||
private int mSessionId = -1;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mDevice.pressHome();
|
||||
waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
|
||||
waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
|
||||
mSessionId = -1;
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (mSessionId > -1) {
|
||||
mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a session and return the id.
|
||||
*/
|
||||
private int createSession(String label, Bitmap icon) throws Throwable {
|
||||
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||
params.setAppPackageName("test.promise.app");
|
||||
params.setAppLabel(label);
|
||||
params.setAppIcon(icon);
|
||||
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
|
||||
return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
|
||||
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
|
||||
final Workspace.ItemOperator findPromiseApp = (info, view) ->
|
||||
info != null && TextUtils.equals(info.title, appLabel);
|
||||
|
||||
// Create and add test session
|
||||
mSessionId = createSession(appLabel, Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
|
||||
|
||||
// Verify promise icon is added
|
||||
waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
|
||||
launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
|
||||
|
||||
// Remove session
|
||||
mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
|
||||
mSessionId = -1;
|
||||
|
||||
// Verify promise icon is removed
|
||||
waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
|
||||
launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
|
||||
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
|
||||
final Workspace.ItemOperator findPromiseApp = (info, view) ->
|
||||
info != null && TextUtils.equals(info.title, appLabel);
|
||||
|
||||
// Create and add test session without icon or label
|
||||
mSessionId = createSession(null, null);
|
||||
|
||||
// Sleep for duration of animation if a view was to be added + some buffer time.
|
||||
Thread.sleep(Launcher.NEW_APPS_PAGE_MOVE_DELAY + Launcher.NEW_APPS_ANIMATION_DELAY + 500);
|
||||
|
||||
// Verify promise icon is not added
|
||||
waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
|
||||
launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static java.lang.System.exit;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -112,6 +111,7 @@ public abstract class AbstractLauncherUiTest {
|
||||
launcher ->
|
||||
checkLauncherIntegrity(launcher, containerType)));
|
||||
}
|
||||
mLauncher.enableDebugTracing();
|
||||
}
|
||||
|
||||
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
|
||||
@@ -187,14 +187,6 @@ public abstract class AbstractLauncherUiTest {
|
||||
}
|
||||
}
|
||||
|
||||
protected void lockRotation(boolean naturalOrientation) throws RemoteException {
|
||||
if (naturalOrientation) {
|
||||
mDevice.setOrientationNatural();
|
||||
} else {
|
||||
mDevice.setOrientationRight();
|
||||
}
|
||||
}
|
||||
|
||||
protected void clearLauncherData() throws IOException, InterruptedException {
|
||||
if (TestHelpers.isInLauncherProcess()) {
|
||||
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
|
||||
@@ -202,6 +194,7 @@ public abstract class AbstractLauncherUiTest {
|
||||
resetLoaderState();
|
||||
} else {
|
||||
clearPackageData(mDevice.getLauncherPackageName());
|
||||
mLauncher.enableDebugTracing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +266,12 @@ public abstract class AbstractLauncherUiTest {
|
||||
waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected <T> T getOnceNotNull(String message, Function<Launcher, T> f) {
|
||||
return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected void waitForLauncherCondition(
|
||||
@@ -281,6 +280,20 @@ public abstract class AbstractLauncherUiTest {
|
||||
Wait.atMost(message, () -> getFromLauncher(condition), timeout);
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected <T> T getOnceNotNull(String message, Function<Launcher, T> f, long timeout) {
|
||||
if (!TestHelpers.isInLauncherProcess()) return null;
|
||||
|
||||
final Object[] output = new Object[1];
|
||||
Wait.atMost(message, () -> {
|
||||
final Object fromLauncher = getFromLauncher(f);
|
||||
output[0] = fromLauncher;
|
||||
return fromLauncher != null;
|
||||
}, timeout);
|
||||
return (T) output[0];
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected void waitForLauncherCondition(
|
||||
|
||||
@@ -44,8 +44,11 @@ class PortraitLandscapeRunner implements TestRule {
|
||||
} finally {
|
||||
mTest.mDevice.setOrientationNatural();
|
||||
mTest.executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(
|
||||
false));
|
||||
{
|
||||
if (launcher != null) {
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(false);
|
||||
}
|
||||
});
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
|
||||
@Test
|
||||
public void testWorkspace() throws Exception {
|
||||
mLauncher.enableDebugTracing();
|
||||
final Workspace workspace = mLauncher.getWorkspace();
|
||||
|
||||
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
|
||||
@@ -209,7 +208,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
// Test starting a workspace app.
|
||||
final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
|
||||
assertNotNull("No Chrome app in workspace", app);
|
||||
mLauncher.disableDebugTracing();
|
||||
}
|
||||
|
||||
public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
|
||||
@@ -300,7 +298,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragAppIcon() throws Throwable {
|
||||
mLauncher.enableDebugTracing();
|
||||
// 1. Open all apps and wait for load complete.
|
||||
// 2. Drag icon to homescreen.
|
||||
// 3. Verify that the icon works on homescreen.
|
||||
@@ -317,13 +314,11 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
"Launcher activity is the top activity; expecting another activity to be the top "
|
||||
+ "one",
|
||||
isInBackground(launcher)));
|
||||
mLauncher.disableDebugTracing();
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragShortcut() throws Throwable {
|
||||
mLauncher.enableDebugTracing();
|
||||
// 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
|
||||
@@ -343,7 +338,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
mLauncher.disableDebugTracing();
|
||||
}
|
||||
|
||||
public static String getAppPackageName() {
|
||||
|
||||
@@ -69,34 +69,22 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testWidgetConfig() throws Throwable {
|
||||
runTest(false, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // b/121280703
|
||||
public void testWidgetConfig_rotate() throws Throwable {
|
||||
runTest(true, true);
|
||||
runTest(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testConfigCancelled() throws Throwable {
|
||||
runTest(false, false);
|
||||
runTest(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // b/121280703
|
||||
public void testConfigCancelled_rotate() throws Throwable {
|
||||
runTest(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rotateConfig should the config screen be rotated
|
||||
* @param acceptConfig accept the config activity
|
||||
*/
|
||||
private void runTest(boolean rotateConfig, boolean acceptConfig) throws Throwable {
|
||||
lockRotation(true);
|
||||
|
||||
private void runTest(boolean acceptConfig) throws Throwable {
|
||||
clearHomescreen();
|
||||
mDevice.pressHome();
|
||||
|
||||
@@ -110,13 +98,6 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest {
|
||||
// Widget id for which the config activity was opened
|
||||
mWidgetId = monitor.getWidgetId();
|
||||
|
||||
if (rotateConfig) {
|
||||
// Rotate the screen and verify that the config activity is recreated
|
||||
monitor = new WidgetConfigStartupMonitor();
|
||||
lockRotation(false);
|
||||
assertEquals(mWidgetId, monitor.getWidgetId());
|
||||
}
|
||||
|
||||
// Verify that the widget id is valid and bound
|
||||
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
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.assertTrue;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
@@ -22,6 +25,7 @@ import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.tapl.Widget;
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
import com.android.launcher3.ui.TestViewHelpers;
|
||||
import com.android.launcher3.util.rule.ShellCommandRule;
|
||||
@@ -41,19 +45,8 @@ public class AddWidgetTest extends AbstractLauncherUiTest {
|
||||
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
@Test
|
||||
public void testDragIcon_portrait() throws Throwable {
|
||||
lockRotation(true);
|
||||
performTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // b/121280703
|
||||
public void testDragIcon_landscape() throws Throwable {
|
||||
lockRotation(false);
|
||||
performTest();
|
||||
}
|
||||
|
||||
private void performTest() throws Throwable {
|
||||
@PortraitLandscape
|
||||
public void testDragIcon() throws Throwable {
|
||||
clearHomescreen();
|
||||
mDevice.pressHome();
|
||||
|
||||
@@ -70,5 +63,10 @@ public class AddWidgetTest extends AbstractLauncherUiTest {
|
||||
(info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
|
||||
widgetInfo.provider.getClassName())).call());
|
||||
|
||||
final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
|
||||
DEFAULT_UI_TIMEOUT);
|
||||
assertNotNull("Widget not found on the workspace", widget);
|
||||
widget.launch(getAppPackageName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,6 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return;
|
||||
}
|
||||
lockRotation(true);
|
||||
|
||||
clearHomescreen();
|
||||
mDevice.pressHome();
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ public final class LauncherInstrumentation {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEquals(String message, String expected, String actual) {
|
||||
void assertEquals(String message, String expected, String actual) {
|
||||
if (!TextUtils.equals(expected, actual)) {
|
||||
fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
|
||||
}
|
||||
@@ -763,8 +763,7 @@ public final class LauncherInstrumentation {
|
||||
final Bundle parcel = (Bundle) executeAndWaitForEvent(
|
||||
() -> linearGesture(startX, startY, endX, endY, steps),
|
||||
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
|
||||
"Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
|
||||
+ ", " + endX + ", " + endY);
|
||||
"Swipe failed to receive an event for the swipe end");
|
||||
assertEquals("Swipe switched launcher to a wrong state;",
|
||||
TestProtocol.stateOrdinalToString(expectedState),
|
||||
TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
|
||||
@@ -958,8 +957,8 @@ public final class LauncherInstrumentation {
|
||||
getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
|
||||
}
|
||||
|
||||
public long getAllocatedMemory() {
|
||||
return getTestInfo(TestProtocol.REQUEST_ALLOCATED_MEMORY).
|
||||
getLong(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
public int getTotalPssKb() {
|
||||
return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package com.android.launcher3.tapl;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
@@ -31,7 +32,8 @@ import com.android.launcher3.ResourceUtils;
|
||||
* All widgets container.
|
||||
*/
|
||||
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int FLING_SPEED = 1500;
|
||||
private static final Rect MARGINS = new Rect(100, 100, 100, 100);
|
||||
private static final int FLING_STEPS = 10;
|
||||
|
||||
Widgets(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
@@ -46,11 +48,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
"want to fling forward in widgets")) {
|
||||
LauncherInstrumentation.log("Widgets.flingForward enter");
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
widgetsContainer.setGestureMargins(0, 0, 0,
|
||||
ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
|
||||
mLauncher.getResources()) + 1);
|
||||
widgetsContainer.fling(Direction.DOWN,
|
||||
(int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, FLING_STEPS);
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
|
||||
verifyActiveContainer();
|
||||
}
|
||||
@@ -66,10 +64,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
"want to fling backwards in widgets")) {
|
||||
LauncherInstrumentation.log("Widgets.flingBackward enter");
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
widgetsContainer.setGestureMargin(100);
|
||||
widgetsContainer.fling(Direction.UP,
|
||||
(int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
mLauncher.scroll(widgetsContainer, Direction.UP, 1f, MARGINS, FLING_STEPS);
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
|
||||
verifyActiveContainer();
|
||||
}
|
||||
@@ -82,7 +77,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
return LauncherInstrumentation.ContainerType.WIDGETS;
|
||||
}
|
||||
|
||||
public Widget getWidget(String label) {
|
||||
public Widget getWidget(String labelText) {
|
||||
final int margin = ResourceUtils.getNavbarSize(
|
||||
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
@@ -91,17 +86,24 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
final Point displaySize = mLauncher.getRealDisplaySize();
|
||||
|
||||
int i = 0;
|
||||
final BySelector selector = By.
|
||||
clazz("com.android.launcher3.widget.WidgetCell").
|
||||
hasDescendant(By.text(label));
|
||||
final BySelector selector = By.clazz("android.widget.TextView").text(labelText);
|
||||
|
||||
for (; ; ) {
|
||||
final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300);
|
||||
if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) {
|
||||
return new Widget(mLauncher, widget);
|
||||
final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300);
|
||||
if (label != null) {
|
||||
final UiObject2 widget = label.getParent().getParent();
|
||||
mLauncher.assertEquals(
|
||||
"View is not WidgetCell",
|
||||
"com.android.launcher3.widget.WidgetCell",
|
||||
widget.getClassName());
|
||||
|
||||
if (widget.getVisibleBounds().bottom <= displaySize.y - margin) {
|
||||
return new Widget(mLauncher, widget);
|
||||
}
|
||||
}
|
||||
|
||||
if (++i > 40) fail("Too many attempts");
|
||||
widgetsContainer.scroll(Direction.DOWN, 1f);
|
||||
mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
@@ -40,6 +41,7 @@ public final class Workspace extends Home {
|
||||
private static final float FLING_SPEED =
|
||||
LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
|
||||
private static final int DRAG_DURACTION = 2000;
|
||||
private static final int FLING_STEPS = 10;
|
||||
private final UiObject2 mHotseat;
|
||||
|
||||
Workspace(LauncherInstrumentation launcher) {
|
||||
@@ -180,9 +182,9 @@ public final class Workspace extends Home {
|
||||
*/
|
||||
public void flingForward() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
workspace.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
|
||||
workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
mLauncher.scroll(workspace, Direction.RIGHT, 1f,
|
||||
new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0),
|
||||
FLING_STEPS);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
@@ -192,9 +194,9 @@ public final class Workspace extends Home {
|
||||
*/
|
||||
public void flingBackward() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
workspace.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
|
||||
workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
mLauncher.scroll(workspace, Direction.LEFT, 1f,
|
||||
new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0),
|
||||
FLING_STEPS);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user