Merging from ub-launcher3-master @ build 6369897

Test: manual, presubmit on the source branch
Bug:150504032

x20/teams/android-launcher/merge/ub-launcher3-master_6369897.html

Change-Id: Id94544cf790a7dcf0841f66648ac864bf2f530d4
This commit is contained in:
Matt Casey
2020-04-07 00:54:25 +00:00
86 changed files with 1084 additions and 999 deletions
@@ -209,6 +209,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
WorkspaceItemInfo info = predictions.get(i);
PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
icon.setEnabled(false);
icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
icon.verifyHighRes();
CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
@@ -349,7 +349,10 @@ public class HotseatPredictionController implements DragController.DragListener,
mHotSeatItemsCount);
}
private void pinPrediction(ItemInfo info) {
/**
* Pins a predicted app icon into place.
*/
public void pinPrediction(ItemInfo info) {
PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt(
mHotseat.getCellXFromOrder(info.rank),
mHotseat.getCellYFromOrder(info.rank));
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.uioverrides;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.PIN_PREDICTION;
import static com.android.launcher3.graphics.IconShape.getShape;
import android.content.Context;
@@ -26,15 +27,19 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
@@ -43,7 +48,8 @@ import com.android.launcher3.views.DoubleShadowBubbleTextView;
/**
* A BubbleTextView with a ring around it's drawable
*/
public class PredictedAppIcon extends DoubleShadowBubbleTextView {
public class PredictedAppIcon extends DoubleShadowBubbleTextView implements
LauncherAccessibilityDelegate.AccessibilityActionHandler {
private static final float RING_EFFECT_RATIO = 0.11f;
@@ -97,6 +103,13 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
super.applyFromWorkspaceItem(info);
int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
mIconRingPaint.setColor(ColorUtils.setAlphaComponent(color, 200));
if (mIsPinned) {
setContentDescription(info.contentDescription);
} else {
setContentDescription(
getContext().getString(R.string.hotseat_prediction_content_description,
info.contentDescription));
}
}
/**
@@ -104,9 +117,9 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
*/
public void pin(WorkspaceItemInfo info) {
if (mIsPinned) return;
mIsPinned = true;
applyFromWorkspaceItem(info);
setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
mIsPinned = true;
((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
invalidate();
}
@@ -121,6 +134,27 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
verifyHighRes();
}
@Override
public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) {
accessibilityNodeInfo.addAction(
new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
getContext().getText(R.string.pin_prediction)));
}
@Override
public boolean performAccessibilityAction(int action, ItemInfo info) {
QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext()));
if (action == PIN_PREDICTION) {
if (launcher == null || launcher.getHotseatPredictionController() == null) {
return false;
}
HotseatPredictionController controller = launcher.getHotseatPredictionController();
controller.pinPrediction(info);
return true;
}
return false;
}
@Override
public void getIconBounds(Rect outBounds) {
super.getIconBounds(outBounds);
@@ -19,12 +19,9 @@ import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import androidx.annotation.Nullable;
@@ -38,7 +35,6 @@ import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
@@ -72,77 +68,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
*/
public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
public static final RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.left = top;
out.top = right;
out.right = bottom;
out.bottom = left;
}
@Override
public void mapInsets(Context context, Rect insets, Rect out) {
// If there is a display cutout, the top insets in portrait would also include the
// cutout, which we will get as the left inset in landscape. Using the max of left and
// top allows us to cover both cases (with or without cutout).
if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
out.top = Math.max(insets.top, insets.left);
out.bottom = Math.max(insets.right, insets.bottom);
out.left = out.right = 0;
} else {
out.top = Math.max(insets.top, insets.left);
out.bottom = insets.right;
out.left = insets.bottom;
out.right = 0;
}
}
};
public static final RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.left = bottom;
out.top = left;
out.right = top;
out.bottom = right;
}
@Override
public void mapInsets(Context context, Rect insets, Rect out) {
if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
out.top = Math.max(insets.top, insets.right);
out.bottom = Math.max(insets.left, insets.bottom);
out.left = out.right = 0;
} else {
out.top = Math.max(insets.top, insets.right);
out.bottom = insets.left;
out.right = insets.bottom;
out.left = 0;
}
}
@Override
public int toNaturalGravity(int absoluteGravity) {
int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (horizontalGravity == Gravity.RIGHT) {
horizontalGravity = Gravity.LEFT;
} else if (horizontalGravity == Gravity.LEFT) {
horizontalGravity = Gravity.RIGHT;
}
if (verticalGravity == Gravity.TOP) {
verticalGravity = Gravity.BOTTOM;
} else if (verticalGravity == Gravity.BOTTOM) {
verticalGravity = Gravity.TOP;
}
return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
& ~Gravity.VERTICAL_GRAVITY_MASK)
| horizontalGravity | verticalGravity;
}
};
private HotseatPredictionController mHotseatPredictionController;
@Override
@@ -153,12 +78,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
}
}
@Override
protected RotationMode getFakeRotationMode(DeviceProfile dp) {
return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
: (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -91,7 +92,7 @@ public final class RecentsViewStateController extends
View actionsView = mLauncher.getActionsView();
if (actionsView != null) {
propertySetter.setViewAlpha(actionsView, buttonAlpha, actionInterpolator);
propertySetter.setFloat(actionsView, VIEW_ALPHA, buttonAlpha, actionInterpolator);
}
}
@@ -46,7 +46,6 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
@@ -149,8 +148,8 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode navBarRotationMode) {
return mRecentsView != null ? mRecentsView.getEventDispatcher(navBarRotationMode) : null;
public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
}
@UiThread
@@ -444,9 +444,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
GestureState newGestureState;
if (mDeviceState.isInSwipeUpTouchRegion(event)) {
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
// onConsumerInactive and wipe the previous gesture state
GestureState prevGestureState = new GestureState(mGestureState);
newGestureState = createGestureState();
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(mGestureState, newGestureState, event);
mConsumer = newConsumer(prevGestureState, newGestureState, event);
ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
mUncheckedConsumer = mConsumer;
@@ -678,7 +681,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
* To be called by the consumer when it's no longer active.
*/
private void onConsumerInactive(InputConsumer caller) {
if (mConsumer == caller) {
if (mConsumer != null && mConsumer.isInConsumerHierarchy(caller)) {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
mGestureState = new GestureState();
}
@@ -29,6 +29,11 @@ public abstract class DelegateInputConsumer implements InputConsumer {
return mDelegate.isConsumerDetachedFromGesture();
}
@Override
public boolean isInConsumerHierarchy(InputConsumer candidate) {
return this == candidate || mDelegate.isInConsumerHierarchy(candidate);
}
@Override
public boolean allowInterceptByParent() {
return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
@@ -182,7 +182,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler
.getRecentsViewDispatcher(mNavBarPosition.getRotationMode()));
.getRecentsViewDispatcher(mNavBarPosition.getRotation()));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -42,7 +42,6 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
@@ -172,7 +171,7 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt(
mActivity.findViewById(R.id.scrim_view), ScrimView.DRAG_HANDLE_ALPHA, 0);
mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0);
dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
anim.play(dragHandleAnim);
@@ -71,6 +71,7 @@ import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -90,6 +91,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PendingAnimation.EndState;
@@ -97,9 +99,9 @@ import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -169,6 +171,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
};
private OrientationEventListener mOrientationListener;
private int mPreviousRotation;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
@@ -376,6 +379,22 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
mOrientationListener = new OrientationEventListener(getContext()) {
@Override
public void onOrientationChanged(int i) {
int rotation = RotationHelper.getRotationFromDegrees(i);
if (mPreviousRotation != rotation) {
animateRecentsRotationInPlace(rotation);
if (rotation == 0) {
showActionsView();
} else {
hideActionsView();
}
mPreviousRotation = rotation;
}
}
};
}
public OverScroller getScroller() {
@@ -504,6 +523,15 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
public void setOverviewStateEnabled(boolean enabled) {
if (supportsVerticalLandscape()
&& !TestProtocol.sDisableSensorRotation // Ignore hardware dependency for tests
&& mOrientationListener.canDetectOrientation()) {
if (enabled) {
mOrientationListener.enable();
} else {
mOrientationListener.disable();
}
}
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
if (!enabled) {
@@ -784,23 +812,14 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
return;
}
CurveProperties curveProperties = mOrientationHandler
.getCurveProperties(this, mInsets);
int scroll = curveProperties.scroll;
final int halfPageSize = curveProperties.halfPageSize;
final int screenCenter = curveProperties.screenCenter;
final int halfScreenSize = curveProperties.halfScreenSize;
final int pageSpacing = mPageSpacing;
mScrollState.scrollFromEdge = mIsRtl ? scroll : (mMaxScroll - scroll);
mOrientationHandler.getCurveProperties(this, mInsets, mScrollState);
mScrollState.scrollFromEdge =
mIsRtl ? mScrollState.scroll : (mMaxScroll - mScrollState.scroll);
final int pageCount = getPageCount();
for (int i = 0; i < pageCount; i++) {
View page = getPageAt(i);
float pageCenter = mOrientationHandler.getViewCenterPosition(page) + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing;
mScrollState.linearInterpolation = Math.min(1,
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
mScrollState.updateInterpolation(mOrientationHandler.getChildStart(page), mPageSpacing);
((PageCallbacks) page).onPageScroll(mScrollState);
}
}
@@ -947,6 +966,35 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
setSwipeDownShouldLaunchApp(true);
}
private void animateRecentsRotationInPlace(int newRotation) {
if (!supportsVerticalLandscape()) {
return;
}
AnimatorSet pa = setRecentsChangedOrientation(true);
pa.addListener(AnimationSuccessListener.forRunnable(() -> {
updateLayoutRotation(newRotation);
mActivity.getDragLayer().recreateControllers();
rotateAllChildTasks();
setRecentsChangedOrientation(false).start();
}));
pa.start();
}
public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) {
getRunningTaskIndex();
int runningIndex = getCurrentPage();
AnimatorSet as = new AnimatorSet();
for (int i = 0; i < getTaskViewCount(); i++) {
if (runningIndex == i) {
continue;
}
View taskView = getTaskViewAt(i);
as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
}
return as;
}
abstract protected boolean supportsVerticalLandscape();
private void rotateAllChildTasks() {
@@ -1143,7 +1191,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
default void onPageScroll(ScrollState scrollState) {}
}
public static class ScrollState {
public static class ScrollState extends CurveProperties {
/**
* The progress from 0 to 1, where 0 is the center
@@ -1155,6 +1203,17 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
* The amount by which all the content is scrolled relative to the end of the list.
*/
public float scrollFromEdge;
/**
* Updates linearInterpolation for the provided child position
*/
public void updateInterpolation(int childStart, int pageSpacing) {
float pageCenter = childStart + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing;
linearInterpolation = Math.min(1,
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
}
}
public void setIgnoreResetTask(int taskId) {
@@ -1924,13 +1983,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return offsetX;
}
public Consumer<MotionEvent> getEventDispatcher(RotationMode navBarRotationMode) {
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
float degreesRotated;
if (navBarRotationMode == RotationMode.NORMAL) {
if (navbarRotation == 0) {
degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 :
RotationHelper.getDegreesFromRotation(mLayoutRotation);
} else {
degreesRotated = -navBarRotationMode.surfaceRotation;
degreesRotated = -navbarRotation;
}
if (degreesRotated == 0) {
return super::onTouchEvent;
@@ -1940,7 +1999,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
// undo that transformation since PagedView also accommodates for the transformation via
// PagedOrientationHandler
return e -> {
if (navBarRotationMode != RotationMode.NORMAL
if (navbarRotation != 0
&& !mOrientationState.areMultipleLayoutOrientationsDisabled()) {
RotationHelper.transformEventForNavBar(e, true);
super.onTouchEvent(e);
-3
View File
@@ -66,9 +66,6 @@
docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
<dimen name="multi_window_task_divider_size">10dp</dimen>
<!-- same as vertical_drag_handle_size -->
<dimen name="shelf_surface_offset">24dp</dimen>
<!-- Assistant Gestures -->
<!-- Distance from the vertical edges of the screen in which assist gestures are recognized -->
<dimen name="gestures_assistant_width">48dp</dimen>
+3
View File
@@ -90,6 +90,9 @@
<!-- tip shown if user declines migration and has some open spots for prediction -->
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
<!-- content description for hotseat items -->
<string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
<string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>
<!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] -->
@@ -88,9 +88,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
super.onCreate(savedInstanceState);
mSystemActions = new SystemActions(this);
SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this)
.addModeChangeListener(this);
getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@@ -141,7 +139,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
public void onNavigationModeChanged(Mode newMode) {
getDragLayer().recreateControllers();
getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON);
}
@Override
@@ -130,6 +130,17 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mGestureId = gestureId;
}
public GestureState(GestureState other) {
mHomeIntent = other.mHomeIntent;
mOverviewIntent = other.mOverviewIntent;
mActivityInterface = other.mActivityInterface;
mStateCallback = other.mStateCallback;
mGestureId = other.mGestureId;
mRunningTask = other.mRunningTask;
mEndTarget = other.mEndTarget;
mFinishingRecentsAnimationTaskId = other.mFinishingRecentsAnimationTaskId;
}
public GestureState() {
// Do nothing, only used for initializing the gesture state prior to user unlock
mHomeIntent = new Intent();
@@ -69,6 +69,13 @@ public interface InputConsumer {
return false;
}
/**
* Returns true if the given input consumer is in the hierarchy of this input consumer.
*/
default boolean isInConsumerHierarchy(InputConsumer candidate) {
return this == candidate;
}
/**
* Called by the event queue when the consumer is about to be switched to a new consumer.
* Consumers should update the state accordingly here before the state is passed to the new
@@ -107,11 +107,11 @@ public class BackGestureTutorialFragment extends Fragment implements BackGesture
}
void onAttachedToWindow() {
mEdgeBackGestureHandler.setIsEnabled(true);
mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView());
}
void onDetachedFromWindow() {
mEdgeBackGestureHandler.setIsEnabled(false);
mEdgeBackGestureHandler.setViewGroupParent(null);
}
@Override
@@ -17,21 +17,18 @@ package com.android.quickstep.interaction;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemProperties;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import androidx.annotation.Nullable;
import com.android.launcher3.ResourceUtils;
@@ -40,7 +37,7 @@ import com.android.launcher3.ResourceUtils;
*
* Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java.
*/
public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener {
public class EdgeBackGestureHandler implements OnTouchListener {
private static final String TAG = "EdgeBackGestureHandler";
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
@@ -106,29 +103,22 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener
mEdgeWidth = ResourceUtils.getNavbarSize("config_backGestureInset", res);
}
void setIsEnabled(boolean isEnabled) {
if (isEnabled == mIsEnabled) {
return;
}
mIsEnabled = isEnabled;
void setViewGroupParent(@Nullable ViewGroup parent) {
mIsEnabled = parent != null;
if (mEdgeBackPanel != null) {
mEdgeBackPanel.onDestroy();
mEdgeBackPanel = null;
}
if (!mIsEnabled) {
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
} else {
updateDisplaySize();
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
new Handler(Looper.getMainLooper()));
if (mIsEnabled) {
// Add a nav bar panel window.
mEdgeBackPanel = new EdgeBackGesturePanel(mContext);
mEdgeBackPanel = new EdgeBackGesturePanel(mContext, parent, createLayoutParams());
mEdgeBackPanel.setBackCallback(mBackCallback);
mEdgeBackPanel.setLayoutParams(createLayoutParams());
updateDisplaySize();
if (mContext.getDisplay() != null) {
mContext.getDisplay().getRealSize(mDisplaySize);
mEdgeBackPanel.setDisplaySize(mDisplaySize);
}
}
}
@@ -136,21 +126,11 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener
mGestureCallback = callback;
}
private WindowManager.LayoutParams createLayoutParams() {
private LayoutParams createLayoutParams() {
Resources resources = mContext.getResources();
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
return new LayoutParams(
ResourceUtils.getNavbarSize("navigation_edge_panel_width", resources),
ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources),
LayoutParams.TYPE_APPLICATION_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
layoutParams.setTitle(TAG + mDisplayId);
layoutParams.windowAnimations = 0;
layoutParams.setFitInsetsTypes(0 /* types */);
return layoutParams;
ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources));
}
@Override
@@ -232,26 +212,6 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener
}
}
@Override
public void onDisplayAdded(int displayId) { }
@Override
public void onDisplayRemoved(int displayId) { }
@Override
public void onDisplayChanged(int displayId) {
if (displayId == mDisplayId) {
updateDisplaySize();
}
}
private void updateDisplaySize() {
mContext.getDisplay().getRealSize(mDisplaySize);
if (mEdgeBackPanel != null) {
mEdgeBackPanel.setDisplaySize(mDisplaySize);
}
}
void setInsets(int leftInset, int rightInset) {
mLeftInset = leftInset;
mRightInset = rightInset;
@@ -26,11 +26,11 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.os.SystemClock;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -110,7 +110,6 @@ public class EdgeBackGesturePanel extends View {
private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR =
new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f);
private final WindowManager mWindowManager;
private BackCallback mBackCallback;
/**
@@ -147,7 +146,6 @@ public class EdgeBackGesturePanel extends View {
private VelocityTracker mVelocityTracker;
private int mArrowPaddingEnd;
private WindowManager.LayoutParams mLayoutParams;
/**
* True if the panel is currently on the left of the screen
@@ -232,11 +230,9 @@ public class EdgeBackGesturePanel extends View {
}
};
public EdgeBackGesturePanel(Context context) {
public EdgeBackGesturePanel(Context context, ViewGroup parent, LayoutParams layoutParams) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
mDensity = context.getResources().getDisplayMetrics().density;
mBaseTranslation = dp(BASE_TRANSLATION_DP);
@@ -290,11 +286,15 @@ public class EdgeBackGesturePanel extends View {
mSwipeThreshold = ResourceUtils.getDimenByName(
"navigation_edge_action_drag_threshold", context.getResources(), 16 /* defaultValue */);
parent.addView(this, layoutParams);
setVisibility(GONE);
}
void onDestroy() {
mWindowManager.removeView(this);
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);
}
}
@Override
@@ -305,9 +305,6 @@ public class EdgeBackGesturePanel extends View {
@SuppressLint("RtlHardcoded")
void setIsLeftPanel(boolean isLeftPanel) {
mIsLeftPanel = isLeftPanel;
mLayoutParams.gravity = mIsLeftPanel
? (Gravity.LEFT | Gravity.TOP)
: (Gravity.RIGHT | Gravity.TOP);
}
boolean getIsLeftPanel() {
@@ -323,11 +320,6 @@ public class EdgeBackGesturePanel extends View {
mBackCallback = callback;
}
void setLayoutParams(WindowManager.LayoutParams layoutParams) {
mLayoutParams = layoutParams;
mWindowManager.addView(this, mLayoutParams);
}
private float getCurrentAngle() {
return mCurrentAngle;
}
@@ -349,7 +341,6 @@ public class EdgeBackGesturePanel extends View {
mStartY = event.getY();
setVisibility(VISIBLE);
updatePosition(event.getY());
mWindowManager.updateViewLayout(this, mLayoutParams);
break;
case MotionEvent.ACTION_MOVE:
handleMoveEvent(event);
@@ -614,10 +605,11 @@ public class EdgeBackGesturePanel extends View {
}
private void updatePosition(float touchY) {
float position = touchY - mFingerOffset;
position = Math.max(position, mMinArrowPosition);
position -= mLayoutParams.height / 2.0f;
mLayoutParams.y = MathUtils.clamp((int) position, 0, mDisplaySize.y);
float positionY = touchY - mFingerOffset;
positionY = Math.max(positionY, mMinArrowPosition);
positionY -= getLayoutParams().height / 2.0f;
setX(mIsLeftPanel ? 0 : mDisplaySize.x - getLayoutParams().width);
setY(MathUtils.clamp((int) positionY, 0, mDisplaySize.y));
}
private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) {
@@ -33,9 +33,6 @@ import com.android.quickstep.SysUINavigationMode;
import java.lang.annotation.Retention;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
public class LayoutUtils {
private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
@@ -68,7 +65,7 @@ public class LayoutUtils {
// UI when shown.
extraSpace = 0;
} else {
extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx
extraSpace = getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
+ res.getDimensionPixelSize(
R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
@@ -17,12 +17,8 @@ package com.android.quickstep.util;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
import android.graphics.Rect;
import android.view.Gravity;
import android.view.Surface;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.SysUINavigationMode;
@@ -31,79 +27,6 @@ import com.android.quickstep.SysUINavigationMode;
*/
public class NavBarPosition {
public static final RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.left = top;
out.top = right;
out.right = bottom;
out.bottom = left;
}
@Override
public void mapInsets(Context context, Rect insets, Rect out) {
// If there is a display cutout, the top insets in portrait would also include the
// cutout, which we will get as the left inset in landscape. Using the max of left and
// top allows us to cover both cases (with or without cutout).
if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
out.top = Math.max(insets.top, insets.left);
out.bottom = Math.max(insets.right, insets.bottom);
out.left = out.right = 0;
} else {
out.top = Math.max(insets.top, insets.left);
out.bottom = insets.right;
out.left = insets.bottom;
out.right = 0;
}
}
};
public static final RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.left = bottom;
out.top = left;
out.right = top;
out.bottom = right;
}
@Override
public void mapInsets(Context context, Rect insets, Rect out) {
if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
out.top = Math.max(insets.top, insets.right);
out.bottom = Math.max(insets.left, insets.bottom);
out.left = out.right = 0;
} else {
out.top = Math.max(insets.top, insets.right);
out.bottom = insets.left;
out.right = insets.bottom;
out.left = 0;
}
}
@Override
public int toNaturalGravity(int absoluteGravity) {
int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (horizontalGravity == Gravity.RIGHT) {
horizontalGravity = Gravity.LEFT;
} else if (horizontalGravity == Gravity.LEFT) {
horizontalGravity = Gravity.RIGHT;
}
if (verticalGravity == Gravity.TOP) {
verticalGravity = Gravity.BOTTOM;
} else if (verticalGravity == Gravity.BOTTOM) {
verticalGravity = Gravity.TOP;
}
return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
& ~Gravity.VERTICAL_GRAVITY_MASK)
| horizontalGravity | verticalGravity;
}
};
private final SysUINavigationMode.Mode mMode;
private final int mDisplayRotation;
@@ -120,8 +43,7 @@ public class NavBarPosition {
return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
}
public RotationMode getRotationMode() {
return isLeftEdge() ? ROTATION_SEASCAPE
: (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
public float getRotation() {
return isLeftEdge() ? 90 : (isRightEdge() ? -90 : 0);
}
}
@@ -88,7 +88,6 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
private float mShiftRange;
private final float mShelfOffset;
private float mTopOffset;
private float mShelfTop;
private float mShelfTopAtThreshold;
@@ -110,7 +109,6 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset);
// Just assume the easiest UI for now, until we have the proper layout information.
mDrawingFlatColor = true;
}
@@ -179,7 +177,7 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
}
mTopOffset = dp.getInsets().top - mShelfOffset;
mTopOffset = dp.getInsets().top - mDragHandleSize.y;
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
}
updateColors();
@@ -190,12 +188,15 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
@Override
public void updateColors() {
super.updateColors();
mDragHandleOffset = 0;
if (mDrawingFlatColor) {
mDragHandleOffset = 0;
return;
}
mDragHandleOffset = mShelfOffset - mDragHandleSize;
if (mProgress < mDragHandleProgress) {
mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress);
}
if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
mShelfTop = mShiftRange * mProgress + mTopOffset;
} else {
@@ -231,10 +232,6 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
(float) 0, LINEAR));
mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
}
if (mProgress < mDragHandleProgress) {
mDragHandleOffset += mShiftRange * (mDragHandleProgress - mProgress);
}
}
@Override
@@ -290,4 +287,9 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mPaint.setColor(mShelfColor);
canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
}
@Override
public float getVisualTop() {
return mShelfTop;
}
}
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.graphics.ShadowDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/drag_handle_indicator_no_shadow"
android:elevation="@dimen/vertical_drag_handle_elevation" />
-40
View File
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/vertical_drag_handle_size"
android:height="@dimen/vertical_drag_handle_size"
android:viewportWidth="36.0"
android:viewportHeight="36.0" >
<group
android:translateX="11.5"
android:translateY="11.5">
<path
android:pathData="M2 8.5L6.5 4L11 8.5"
android:strokeColor="?attr/workspaceAmbientShadowColor"
android:strokeWidth="3.6"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:pathData="M2 8.5L6.5 4L11 8.5"
android:strokeColor="?attr/workspaceTextColor"
android:strokeWidth="1.8"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</group>
</vector>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/vertical_drag_handle_width"
android:height="@dimen/vertical_drag_handle_height"
android:viewportWidth="18.0"
android:viewportHeight="6.0"
android:tint="?attr/workspaceTextColor" >
<path
android:pathData="M17,6c-0.15,0-0.3-0.03-0.45-0.11L9,2.12L1.45,5.89c-0.5,0.25-1.09,
0.05-1.34-0.45S0.06,4.35,0.55,4.11l8-4c0.28-0.14,0.61-0.14,0.89,0l8,4c0.49,0.25,0.69,
0.85,0.45,1.34C17.72,5.8,17.37,6,17,6z"
android:fillColor="@android:color/white" />
</vector>
+1 -1
View File
@@ -57,7 +57,7 @@
<com.android.launcher3.pageindicators.WorkspacePageIndicator
android:id="@+id/page_indicator"
android:layout_width="match_parent"
android:layout_height="@dimen/vertical_drag_handle_size"
android:layout_height="@dimen/workspace_page_indicator_height"
android:layout_gravity="bottom|center_horizontal"
android:theme="@style/HomeScreenElementTheme" />
+1
View File
@@ -109,6 +109,7 @@
<item type="id" name="action_dismiss_notification" />
<item type="id" name="action_remote_action_shortcut" />
<item type="id" name="action_dismiss_prediction" />
<item type="id" name="action_pin_prediction"/>
<!-- QSB IDs. DO not change -->
<item type="id" name="search_container_workspace" />
+10 -3
View File
@@ -20,7 +20,6 @@
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">8dp</dimen>
<dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
<!-- Minimum space between workspace and hotseat in spring loaded mode -->
<dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
@@ -36,10 +35,18 @@
<dimen name="dynamic_grid_hotseat_extra_vertical_size">34dp</dimen>
<dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
<!-- Workspace page indicator -->
<dimen name="workspace_page_indicator_height">24dp</dimen>
<dimen name="workspace_page_indicator_line_height">1dp</dimen>
<dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
<!-- Hotseat/all-apps scrim -->
<dimen name="all_apps_scrim_blur">4dp</dimen>
<dimen name="vertical_drag_handle_size">24dp</dimen>
<dimen name="vertical_drag_handle_overlap_workspace">0dp</dimen>
<dimen name="vertical_drag_handle_width">18dp</dimen>
<dimen name="vertical_drag_handle_height">6dp</dimen>
<dimen name="vertical_drag_handle_elevation">1dp</dimen>
<dimen name="vertical_drag_handle_touch_size">48dp</dimen>
<dimen name="vertical_drag_handle_padding_in_vertical_bar_layout">16dp</dimen>
<!-- Drop target bar -->
<dimen name="dynamic_grid_drop_target_size">48dp</dimen>
+1
View File
@@ -18,4 +18,5 @@
<drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
<drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
<drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
<drawable name="all_apps_arrow_shadow">@drawable/drag_handle_indicator_no_shadow</drawable>
</resources>
@@ -30,11 +30,8 @@ import android.content.pm.PackageInstaller.SessionParams;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
@@ -46,8 +43,6 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import java.util.ArrayList;
/**
* Tests for layout parser for remote layout
*/
@@ -120,18 +115,6 @@ public class DefaultLayoutProviderTest {
}
private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception {
mModelHelper.setupDefaultLayoutProvider(builder);
LoaderResults results = new LoaderResults(
LauncherAppState.getInstance(mTargetContext),
mModelHelper.getBgDataModel(),
mModelHelper.getAllAppsList(),
new Callbacks[0]);
LoaderTask task = new LoaderTask(
LauncherAppState.getInstance(mTargetContext),
mModelHelper.getAllAppsList(),
mModelHelper.getBgDataModel(),
results);
Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
mModelHelper.setupDefaultLayoutProvider(builder).loadModelSync();
}
}
@@ -127,6 +127,10 @@ public class LShadowLauncherApps extends ShadowLauncherApps {
@Override
protected List<LauncherActivityInfo> getShortcutConfigActivityList(String packageName,
UserHandle user) {
return Collections.emptyList();
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName);
return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
.stream()
.map(ri -> getLauncherActivityInfo(ri.activityInfo))
.collect(Collectors.toList());
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 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.shadows;
import android.graphics.Typeface;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowTypeface;
/**
* Extension of {@link ShadowTypeface} with missing shadow methods
*/
@Implements(Typeface.class)
public class LShadowTypeface extends ShadowTypeface {
@Implementation
public static Typeface create(Typeface family, int weight, boolean italic) {
int style = italic ? Typeface.ITALIC : Typeface.NORMAL;
if (weight >= 400) {
style |= Typeface.BOLD;
}
return create(family, style);
}
}
@@ -0,0 +1,56 @@
/*
* 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.shadows;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.app.WallpaperManager;
import android.content.Context;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowUserManager;
import org.robolectric.shadows.ShadowWallpaperManager;
/**
* Extension of {@link ShadowUserManager} with missing shadow methods
*/
@Implements(WallpaperManager.class)
public class LShadowWallpaperManager extends ShadowWallpaperManager {
@Implementation
protected static WallpaperManager getInstance(Context context) {
return context.getSystemService(WallpaperManager.class);
}
/**
* Remove this once the fix for
* https://github.com/robolectric/robolectric/issues/5285
* is available
*/
public static void initializeMock() {
WallpaperManager wm = mock(WallpaperManager.class);
ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
shadowApplication.setSystemService(Context.WALLPAPER_SERVICE, wm);
doReturn(0).when(wm).getDesiredMinimumWidth();
doReturn(0).when(wm).getDesiredMinimumHeight();
}
}
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2020 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.shadows;
import android.content.Context;
import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import java.util.HashMap;
import java.util.Map;
/**
* Shadow for {@link Overrides} to provide custom overrides for test
*/
@Implements(value = Overrides.class, isInAndroidSdk = false)
public class ShadowOverrides {
private static Map<Class, ObjectProvider> sProviderMap = new HashMap<>();
@Implementation
public static <T extends ResourceBasedOverride> T getObject(
Class<T> clazz, Context context, int resId) {
ObjectProvider<T> provider = sProviderMap.get(clazz);
if (provider != null) {
return provider.get(context);
}
return Shadow.directlyOn(Overrides.class, "getObject",
ClassParameter.from(Class.class, clazz),
ClassParameter.from(Context.class, context),
ClassParameter.from(int.class, resId));
}
public static <T> void setProvider(Class<T> clazz, ObjectProvider<T> provider) {
sProviderMap.put(clazz, provider);
}
public static void clearProvider() {
sProviderMap.clear();
}
}
@@ -0,0 +1,207 @@
/*
* Copyright (C) 2020 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.ui;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.mock;
import android.app.Activity;
import android.content.Context;
import android.os.SystemClock;
import android.provider.Settings;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerProperties;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetsFullSheet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.util.ReflectionHelpers;
/**
* Tests scroll behavior at various Launcher UI components
*/
@RunWith(LauncherRoboTestRunner.class)
@LooperMode(Mode.PAUSED)
public class LauncherUIScrollTest {
private Context mTargetContext;
private InvariantDeviceProfile mIdp;
private LauncherModelHelper mModelHelper;
private LauncherLayoutBuilder mLayoutBuilder;
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
mTargetContext = RuntimeEnvironment.application;
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
ShadowOverrides.setProvider(UserEventDispatcher.class,
c -> mock(UserEventDispatcher.class));
Settings.Global.putFloat(mTargetContext.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 0);
mModelHelper.installApp(TEST_PACKAGE);
// LayoutBuilder with 3 workspace pages
mLayoutBuilder = new LauncherLayoutBuilder()
.atWorkspace(0, mIdp.numRows - 1, 0).putApp(TEST_PACKAGE, TEST_PACKAGE)
.atWorkspace(0, mIdp.numRows - 1, 1).putApp(TEST_PACKAGE, TEST_PACKAGE)
.atWorkspace(0, mIdp.numRows - 1, 2).putApp(TEST_PACKAGE, TEST_PACKAGE);
}
@Test
public void testWorkspacePagesBound() throws Exception {
// Verify that the workspace if bound synchronously
Launcher launcher = loadLauncher();
assertEquals(3, launcher.getWorkspace().getPageCount());
assertEquals(0, launcher.getWorkspace().getCurrentPage());
launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
assertNotEquals("Workspace was not scrolled",
0, launcher.getWorkspace().getNextPage());
}
@Test
public void testAllAppsScroll() throws Exception {
// Install 100 apps
for (int i = 0; i < 100; i++) {
mModelHelper.installApp(TEST_PACKAGE + i);
}
// Bind and open all-apps
Launcher launcher = loadLauncher();
launcher.getStateManager().goToState(LauncherState.ALL_APPS, false);
doLayout(launcher);
int currentScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
int newScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
assertNotEquals("All Apps was not scrolled", currentScroll, newScroll);
assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
}
@Test
public void testWidgetsListScroll() throws Exception {
// Install 100 widgets
for (int i = 0; i < 100; i++) {
mModelHelper.installCustomShortcut(TEST_PACKAGE + i, "shortcutProvider");
}
// Bind and open widgets
Launcher launcher = loadLauncher();
WidgetsFullSheet widgets = WidgetsFullSheet.show(launcher, false);
doLayout(launcher);
int currentScroll = widgets.getRecyclerView().getCurrentScrollY();
launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
int newScroll = widgets.getRecyclerView().getCurrentScrollY();
assertNotEquals("Widgets was not scrolled", currentScroll, newScroll);
assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
}
@Test
public void testFolderPageScroll() throws Exception {
// Add a folder with multiple icons
FolderBuilder fb = mLayoutBuilder.atWorkspace(mIdp.numColumns / 2, mIdp.numRows / 2, 0)
.putFolder(0);
for (int i = 0; i < 100; i++) {
fb.addApp(TEST_PACKAGE, TEST_PACKAGE);
}
// Bind and open folder
Launcher launcher = loadLauncher();
doLayout(launcher);
launcher.getWorkspace().getFirstMatch((i, v) -> v instanceof FolderIcon).performClick();
ShadowLooper.idleMainLooper();
doLayout(launcher);
FolderPagedView folderPages = Folder.getOpen(launcher).getContent();
assertEquals(0, folderPages.getNextPage());
launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
assertNotEquals("Folder page was not scrolled", 0, folderPages.getNextPage());
assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
}
private Launcher loadLauncher() throws Exception {
mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync();
Launcher launcher = Robolectric.buildActivity(Launcher.class).setup().get();
doLayout(launcher);
ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
if (executor != null) {
executor.runAllTasks();
}
return launcher;
}
private static void doLayout(Activity activity) {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE
.get(RuntimeEnvironment.application).portraitProfile;
View view = activity.getWindow().getDecorView();
view.measure(makeMeasureSpec(dp.widthPx, EXACTLY), makeMeasureSpec(dp.heightPx, EXACTLY));
view.layout(0, 0, dp.widthPx, dp.heightPx);
ShadowLooper.idleMainLooper();
}
private static MotionEvent createScrollEvent(int scroll) {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE
.get(RuntimeEnvironment.application).portraitProfile;
final PointerProperties[] pointerProperties = new PointerProperties[1];
pointerProperties[0] = new PointerProperties();
pointerProperties[0].id = 0;
final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
coords[0] = new MotionEvent.PointerCoords();
coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, scroll);
coords[0].x = dp.widthPx / 2;
coords[0].y = dp.heightPx / 2;
final long time = SystemClock.uptimeMillis();
return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1,
pointerProperties, coords, 0, 0, 1.0f, 1.0f, 0, 0,
InputDevice.SOURCE_CLASS_POINTER, 0);
}
}
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.util;
import static android.content.Intent.ACTION_CREATE_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -44,6 +46,7 @@ import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.pm.UserCache;
import org.mockito.ArgumentCaptor;
@@ -61,6 +64,7 @@ import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.function.Function;
@@ -345,7 +349,8 @@ public class LauncherModelHelper {
/**
* Sets up a dummy provider to load the provided layout by default, next time the layout loads
*/
public void setupDefaultLayoutProvider(LauncherLayoutBuilder builder) throws Exception {
public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
throws Exception {
Context context = RuntimeEnvironment.application;
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE;
@@ -363,22 +368,52 @@ public class LauncherModelHelper {
Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, context);
shadowOf(context.getContentResolver()).registerInputStream(layoutUri,
new ByteArrayInputStream(bos.toByteArray()));
return this;
}
/**
* Simulates an apk install with a default main activity with same class and package name
*/
public void installApp(String component) throws NameNotFoundException {
ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager());
ComponentName cn = new ComponentName(component, component);
spm.addActivityIfNotPresent(cn);
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_LAUNCHER);
installApp(component, component, filter);
}
/**
* Simulates a custom shortcut install
*/
public void installCustomShortcut(String pkg, String clazz) throws NameNotFoundException {
installApp(pkg, clazz, new IntentFilter(ACTION_CREATE_SHORTCUT));
}
private void installApp(String pkg, String clazz, IntentFilter filter)
throws NameNotFoundException {
ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager());
ComponentName cn = new ComponentName(pkg, clazz);
spm.addActivityIfNotPresent(cn);
filter.addCategory(Intent.CATEGORY_DEFAULT);
spm.addIntentFilterForActivity(cn, filter);
}
/**
* Loads the model in memory synchronously
*/
public void loadModelSync() throws ExecutionException, InterruptedException {
// Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread,
// so that we can wait appropriately for the loader to complete.
ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.UI_HELPER_EXECUTOR);
Callbacks mockCb = mock(Callbacks.class);
getModel().addCallbacksAndLoad(mockCb);
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
Executors.UI_HELPER_EXECUTOR.submit(() -> { }).get();
ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.MAIN_EXECUTOR);
getModel().removeCallbacks(mockCb);
}
/**
* An extension of LauncherProvider backed up by in-memory database.
*/
@@ -21,10 +21,13 @@ import com.android.launcher3.shadows.LShadowAppWidgetManager;
import com.android.launcher3.shadows.LShadowBackupManager;
import com.android.launcher3.shadows.LShadowBitmap;
import com.android.launcher3.shadows.LShadowLauncherApps;
import com.android.launcher3.shadows.LShadowTypeface;
import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.shadows.LShadowWallpaperManager;
import com.android.launcher3.shadows.ShadowDeviceFlag;
import com.android.launcher3.shadows.ShadowLooperExecutor;
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import org.junit.runners.model.InitializationError;
@@ -49,9 +52,12 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner {
LShadowLauncherApps.class,
LShadowBitmap.class,
LShadowBackupManager.class,
LShadowTypeface.class,
LShadowWallpaperManager.class,
ShadowLooperExecutor.class,
ShadowMainThreadInitializedObject.class,
ShadowDeviceFlag.class,
ShadowOverrides.class
};
public LauncherRoboTestRunner(Class<?> testClass) throws InitializationError {
@@ -78,6 +84,9 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner {
// Disable plugins
PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
// Initialize mock wallpaper manager
LShadowWallpaperManager.initializeMock();
}
@Override
@@ -86,6 +95,7 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner {
ShadowLog.stream = null;
ShadowMainThreadInitializedObject.resetInitializedObjects();
ShadowOverrides.clearProvider();
}
}
}
@@ -151,27 +151,25 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
DeviceProfile grid = mActivity.getDeviceProfile();
mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
final int defaultIconSize;
if (mDisplay == DISPLAY_WORKSPACE) {
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
} else if (mDisplay == DISPLAY_ALL_APPS) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
} else if (mDisplay == DISPLAY_FOLDER) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
} else {
// widget_selection or shortcut_popup
defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
defaultIconSize = grid.iconSizePx;
}
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
+4 -41
View File
@@ -59,14 +59,12 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.lang.annotation.Retention;
@@ -77,7 +75,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Stack;
public class CellLayout extends ViewGroup implements Transposable {
public class CellLayout extends ViewGroup {
private static final String TAG = "CellLayout";
private static final boolean LOGD = false;
@@ -184,7 +182,6 @@ public class CellLayout extends ViewGroup implements Transposable {
// Related to accessible drag and drop
private boolean mUseTouchHelper = false;
private RotationMode mRotationMode = RotationMode.NORMAL;
public CellLayout(Context context) {
this(context, null);
@@ -206,7 +203,7 @@ public class CellLayout extends ViewGroup implements Transposable {
setClipToPadding(false);
mActivity = ActivityContext.lookupContext(context);
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
DeviceProfile grid = mActivity.getDeviceProfile();
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
@@ -314,24 +311,6 @@ public class CellLayout extends ViewGroup implements Transposable {
}
}
public void setRotationMode(RotationMode mode) {
if (mRotationMode != mode) {
mRotationMode = mode;
requestLayout();
}
}
@Override
public RotationMode getRotationMode() {
return mRotationMode;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
mRotationMode.mapRect(left, top, right, bottom, mTempRect);
super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mUseTouchHelper ||
@@ -789,13 +768,6 @@ public class CellLayout extends ViewGroup implements Transposable {
int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
mShortcutsAndWidgets.setRotation(mRotationMode.surfaceRotation);
if (mRotationMode.isTransposed) {
int tmp = childWidthSize;
childWidthSize = childHeightSize;
childHeightSize = tmp;
}
if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
@@ -846,15 +818,7 @@ public class CellLayout extends ViewGroup implements Transposable {
right + mTempRect.right + getPaddingRight(),
bottom + mTempRect.bottom + getPaddingBottom());
if (mRotationMode.isTransposed) {
int halfW = mShortcutsAndWidgets.getMeasuredWidth() / 2;
int halfH = mShortcutsAndWidgets.getMeasuredHeight() / 2;
int cX = (left + right) / 2;
int cY = (top + bottom) / 2;
mShortcutsAndWidgets.layout(cX - halfW, cY - halfH, cX + halfW, cY + halfH);
} else {
mShortcutsAndWidgets.layout(left, top, right, bottom);
}
mShortcutsAndWidgets.layout(left, top, right, bottom);
}
/**
@@ -863,8 +827,7 @@ public class CellLayout extends ViewGroup implements Transposable {
* width in {@link DeviceProfile#calculateCellWidth(int, int)}.
*/
public int getUnusedHorizontalSpace() {
return (mRotationMode.isTransposed ? getMeasuredHeight() : getMeasuredWidth())
- getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
}
public Drawable getScrimBackground() {
@@ -67,7 +67,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
if (info instanceof WorkspaceItemInfo) {
// Support the action unless the item is in a context menu.
return info.screenId >= 0;
return canRemove(info);
}
return (info instanceof LauncherAppWidgetInfo)
+15 -15
View File
@@ -76,9 +76,9 @@ public class DeviceProfile {
public float workspaceSpringLoadShrinkFactor;
public final int workspaceSpringLoadedBottomSpace;
// Drag handle
public final int verticalDragHandleSizePx;
private final int verticalDragHandleOverlapWorkspace;
// Workspace page indicator
public final int workspacePageIndicatorHeight;
private final int mWorkspacePageIndicatorOverlapWorkspace;
// Workspace icons
public int iconSizePx;
@@ -190,10 +190,10 @@ public class DeviceProfile {
cellLayoutBottomPaddingPx = 0;
}
verticalDragHandleSizePx = res.getDimensionPixelSize(
R.dimen.vertical_drag_handle_size);
verticalDragHandleOverlapWorkspace =
res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace);
workspacePageIndicatorHeight = res.getDimensionPixelSize(
R.dimen.workspace_page_indicator_height);
mWorkspacePageIndicatorOverlapWorkspace =
res.getDimensionPixelSize(R.dimen.workspace_page_indicator_overlap_workspace);
iconDrawablePaddingOriginalPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
@@ -211,7 +211,7 @@ public class DeviceProfile {
hotseatBarSidePaddingEndPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
// Add a bit of space between nav bar and hotseat in vertical bar layout.
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? verticalDragHandleSizePx : 0;
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout()
? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx)
: (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
@@ -227,7 +227,7 @@ public class DeviceProfile {
// in portrait mode closer together by adding more height to the hotseat.
// Note: This calculation was created after noticing a pattern in the design spec.
int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
- verticalDragHandleSizePx;
- workspacePageIndicatorHeight;
hotseatBarSizePx += extraSpace;
hotseatBarBottomPaddingPx += extraSpace;
@@ -376,7 +376,7 @@ public class DeviceProfile {
if (!isVerticalLayout) {
int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
- verticalDragHandleSizePx - edgeMarginPx;
- workspacePageIndicatorHeight - edgeMarginPx;
float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
workspaceSpringLoadShrinkFactor = Math.min(
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -480,14 +480,14 @@ public class DeviceProfile {
padding.bottom = edgeMarginPx;
if (isSeascape()) {
padding.left = hotseatBarSizePx;
padding.right = verticalDragHandleSizePx;
padding.right = hotseatBarSidePaddingStartPx;
} else {
padding.left = verticalDragHandleSizePx;
padding.left = hotseatBarSidePaddingStartPx;
padding.right = hotseatBarSizePx;
}
} else {
int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx
- verticalDragHandleOverlapWorkspace;
int paddingBottom = hotseatBarSizePx + workspacePageIndicatorHeight
- mWorkspacePageIndicatorOverlapWorkspace;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
@@ -554,7 +554,7 @@ public class DeviceProfile {
mInsets.top + dropTargetBarSizePx + edgeMarginPx,
mInsets.left + availableWidthPx - edgeMarginPx,
mInsets.top + availableHeightPx - hotseatBarSizePx
- verticalDragHandleSizePx - edgeMarginPx);
- workspacePageIndicatorHeight - edgeMarginPx);
}
}
+2 -9
View File
@@ -27,15 +27,13 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.Transposable;
import java.util.ArrayList;
public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable {
public class Hotseat extends CellLayout implements LogContainerProvider, Insettable {
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mHasVerticalHotseat;
@@ -89,7 +87,7 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta
@Override
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
DeviceProfile grid = mActivity.getDeviceProfile();
insets = grid.getInsets();
if (grid.isVerticalBarLayout()) {
@@ -117,9 +115,4 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta
public boolean onTouchEvent(MotionEvent event) {
return event.getY() > getCellHeight();
}
@Override
public RotationMode getRotationMode() {
return Launcher.getLauncher(getContext()).getRotationMode();
}
}
+8 -62
View File
@@ -56,7 +56,6 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -102,7 +101,6 @@ import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -317,9 +315,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private float mCurrentAssistantVisibility = 0f;
private DeviceProfile mStableDeviceProfile;
private RotationMode mRotationMode = RotationMode.NORMAL;
protected LauncherOverlayManager mOverlayManager;
// If true, overlay callbacks are deferred
private boolean mDeferOverlayCallbacks;
@@ -503,24 +498,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
super.onConfigurationChanged(newConfig);
}
public void reload() {
onIdpChanged(mDeviceProfile.inv);
}
private boolean supportsFakeLandscapeUI() {
return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate();
}
@Override
public void reapplyUi() {
reapplyUi(true /* cancelCurrentAnimation */);
}
public void reapplyUi(boolean cancelCurrentAnimation) {
if (supportsFakeLandscapeUI()) {
mRotationMode = mStableDeviceProfile == null
? RotationMode.NORMAL : getFakeRotationMode(mDeviceProfile);
}
getRootView().dispatchInsets();
getStateManager().reapplyState(cancelCurrentAnimation);
}
@@ -533,7 +516,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private void onIdpChanged(InvariantDeviceProfile idp) {
mUserEventDispatcher = null;
DeviceProfile oldWallpaperProfile = getWallpaperDeviceProfile();
initDeviceProfile(idp);
dispatchDeviceProfileChanged();
reapplyUi();
@@ -542,9 +524,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// Calling onSaveInstanceState ensures that static cache used by listWidgets is
// initialized properly.
onSaveInstanceState(new Bundle());
if (oldWallpaperProfile != getWallpaperDeviceProfile()) {
mModel.rebindCallbacks();
}
mModel.rebindCallbacks();
}
public void onAssistantVisibilityChanged(float visibility) {
@@ -571,41 +551,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) {
mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
mRotationMode = getFakeRotationMode(mDeviceProfile);
} else {
mStableDeviceProfile = null;
mRotationMode = RotationMode.NORMAL;
}
mRotationHelper.updateRotationAnimation();
onDeviceProfileInitiated();
mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true);
}
public void updateInsets(Rect insets) {
mDeviceProfile.updateInsets(insets);
if (mStableDeviceProfile != null) {
Rect r = mStableDeviceProfile.getInsets();
mRotationMode.mapInsets(this, insets, r);
mStableDeviceProfile.updateInsets(r);
}
}
@Override
public RotationMode getRotationMode() {
return mRotationMode;
}
/**
* Device profile to be used by UI elements which are shown directly on top of the wallpaper
* and whose presentation is tied to the wallpaper (and physical device) and not the activity
* configuration.
*/
@Override
public DeviceProfile getWallpaperDeviceProfile() {
return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile;
mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true);
}
public RotationHelper getRotationHelper() {
@@ -1193,7 +1140,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mAppsView);
mAllAppsController.setupViews(mAppsView, mScrimView);
}
/**
@@ -1415,6 +1362,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return mDropTargetBar;
}
public ScrimView getScrimView() {
return mScrimView;
}
public LauncherAppWidgetHost getAppWidgetHost() {
return mAppWidgetHost;
}
@@ -1753,7 +1704,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
int cellY) {
final FolderInfo folderInfo = new FolderInfo();
folderInfo.title = "";
// Update the model
getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
@@ -2049,7 +1999,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mAppWidgetHost.clearViews();
if (mHotseat != null) {
mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
}
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -2724,10 +2674,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return new TouchController[] {getDragController(), new AllAppsSwipeController(this)};
}
protected RotationMode getFakeRotationMode(DeviceProfile deviceProfile) {
return RotationMode.NORMAL;
}
protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
return new ScaleAndTranslation(1.1f, 0f, 0f);
}
@@ -83,7 +83,7 @@ public class LauncherRootView extends InsettableFrameLayout {
UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0);
// Update device profile before notifying th children.
mLauncher.updateInsets(insets);
mLauncher.getDeviceProfile().updateInsets(insets);
boolean resetState = !insets.equals(mInsets);
setInsets(insets);
@@ -127,7 +127,7 @@ public class LauncherRootView extends InsettableFrameLayout {
}
public void dispatchInsets() {
mLauncher.updateInsets(mInsets);
mLauncher.getDeviceProfile().updateInsets(mInsets);
super.setInsets(mInsets);
}
+18 -16
View File
@@ -16,6 +16,14 @@
package com.android.launcher3;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
import android.animation.LayoutTransition;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
@@ -42,14 +50,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.ScrollView;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
import androidx.annotation.Nullable;
import com.android.launcher3.anim.Interpolators;
@@ -63,6 +63,7 @@ import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
@@ -1369,10 +1370,6 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL: {
Launcher launcher = Launcher.getLauncher(getContext());
if (launcher != null) {
AbstractFloatingView.closeAllOpenViews(launcher);
}
// Handle mouse (or ext. device) by shifting the page depending on the scroll
final float vscroll;
final float hscroll;
@@ -1383,8 +1380,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
}
if (Math.abs(vscroll) > Math.abs(hscroll) && !isVerticalScrollable()) {
return true;
if (!canScroll(Math.abs(vscroll), Math.abs(hscroll))) {
return false;
}
if (hscroll != 0 || vscroll != 0) {
boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0)
@@ -1402,8 +1399,13 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
return super.onGenericMotionEvent(event);
}
protected boolean isVerticalScrollable() {
return true;
/**
* Returns true if the paged view can scroll for the provided vertical and horizontal
* scroll values
*/
protected boolean canScroll(float absVScroll, float absHScroll) {
ActivityContext ac = ActivityContext.lookupContext(getContext());
return (ac == null || AbstractFloatingView.getTopOpenView(ac) == null);
}
private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
@@ -93,7 +93,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
public void setupLp(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (child instanceof LauncherAppWidgetHostView) {
DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
DeviceProfile profile = mActivity.getDeviceProfile();
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
profile.appWidgetScale.x, profile.appWidgetScale.y);
} else {
@@ -108,12 +108,12 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
public int getCellContentHeight() {
return Math.min(getMeasuredHeight(),
mActivity.getWallpaperDeviceProfile().getCellHeight(mContainerType));
mActivity.getDeviceProfile().getCellHeight(mContainerType));
}
public void measureChild(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
final DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
final DeviceProfile profile = mActivity.getDeviceProfile();
if (child instanceof LauncherAppWidgetHostView) {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
+3 -20
View File
@@ -62,7 +62,6 @@ import android.view.animation.Interpolator;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -72,7 +71,6 @@ import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.lang.reflect.Method;
@@ -165,7 +163,7 @@ public final class Utilities {
public static float getDescendantCoordRelativeToAncestor(
View descendant, View ancestor, float[] coord, boolean includeRootScroll) {
return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll,
false, null);
false);
}
/**
@@ -178,15 +176,12 @@ public final class Utilities {
* @param includeRootScroll Whether or not to account for the scroll of the descendant:
* sometimes this is relevant as in a child's coordinates within the descendant.
* @param ignoreTransform If true, view transform is ignored
* @param outRotation If not null, and {@param ignoreTransform} is true, this is set to the
* overall rotation of the view in degrees.
* @return The factor by which this descendant is scaled relative to this DragLayer. Caution
* this scale factor is assumed to be equal in X and Y, and so if at any point this
* assumption fails, we will need to return a pair of scale factors.
*/
public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor,
float[] coord, boolean includeRootScroll, boolean ignoreTransform,
float[] outRotation) {
float[] coord, boolean includeRootScroll, boolean ignoreTransform) {
float scale = 1.0f;
View v = descendant;
while(v != ancestor && v != null) {
@@ -196,19 +191,7 @@ public final class Utilities {
offsetPoints(coord, -v.getScrollX(), -v.getScrollY());
}
if (ignoreTransform) {
if (v instanceof Transposable) {
RotationMode m = ((Transposable) v).getRotationMode();
if (m.isTransposed) {
sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY());
sMatrix.mapPoints(coord);
if (outRotation != null) {
outRotation[0] += m.surfaceRotation;
}
}
}
} else {
if (!ignoreTransform) {
v.getMatrix().mapPoints(coord);
}
offsetPoints(coord, v.getLeft(), v.getTop());
+11 -21
View File
@@ -75,7 +75,6 @@ import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
@@ -125,7 +124,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
private static final int DEFAULT_PAGE = 0;
public static final int DEFAULT_PAGE = 0;
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -276,20 +275,13 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = mLauncher.getDeviceProfile();
DeviceProfile stableGrid = mLauncher.getWallpaperDeviceProfile();
mMaxDistanceForFolderCreation = stableGrid.isTablet
? 0.75f * stableGrid.iconSizePx
: 0.55f * stableGrid.iconSizePx;
mMaxDistanceForFolderCreation = grid.isTablet
? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx;
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
Rect padding = stableGrid.workspacePadding;
RotationMode rotationMode = mLauncher.getRotationMode();
rotationMode.mapRect(padding, mTempRect);
setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
rotationMode.mapRect(stableGrid.getInsets(), mInsets);
Rect padding = grid.workspacePadding;
setPadding(padding.left, padding.top, padding.right, padding.bottom);
if (mWorkspaceFadeInAdjacentScreens) {
// In landscape mode the page spacing is set to the default.
@@ -302,12 +294,11 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
int paddingLeftRight = stableGrid.cellLayoutPaddingLeftRightPx;
int paddingBottom = stableGrid.cellLayoutBottomPaddingPx;
int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
int paddingBottom = grid.cellLayoutBottomPaddingPx;
for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
CellLayout page = mWorkspaceScreens.valueAt(i);
page.setRotationMode(rotationMode);
page.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
mWorkspaceScreens.valueAt(i)
.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
}
}
@@ -327,7 +318,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
float scale = 1;
if (isWidget) {
DeviceProfile profile = mLauncher.getWallpaperDeviceProfile();
DeviceProfile profile = mLauncher.getDeviceProfile();
scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
size[0] = r.width();
@@ -555,10 +546,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
// created CellLayout.
CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
DeviceProfile grid = mLauncher.getWallpaperDeviceProfile();
DeviceProfile grid = mLauncher.getDeviceProfile();
int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
int paddingBottom = grid.cellLayoutBottomPaddingPx;
newScreen.setRotationMode(mLauncher.getRotationMode());
newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
mWorkspaceScreens.put(screenId, newScreen);
@@ -104,10 +104,8 @@ public class WorkspaceStateTransitionAnimation {
Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
if (!hotseat.getRotationMode().isTransposed) {
setPivotToScaleWithWorkspace(hotseat);
setPivotToScaleWithWorkspace(qsbScaleView);
}
setPivotToScaleWithWorkspace(hotseat);
setPivotToScaleWithWorkspace(qsbScaleView);
float hotseatScale = hotseatScaleAndTranslation.scale;
Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
scaleInterpolator);
@@ -56,6 +56,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
public static final int REMOVE = R.id.action_remove;
public static final int UNINSTALL = R.id.action_uninstall;
public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction;
public static final int PIN_PREDICTION = R.id.action_pin_prediction;
public static final int RECONFIGURE = R.id.action_reconfigure;
protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
protected static final int MOVE = R.id.action_move;
@@ -120,6 +121,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
if (!(host.getTag() instanceof ItemInfo)) return;
ItemInfo item = (ItemInfo) host.getTag();
if (host instanceof AccessibilityActionHandler) {
((AccessibilityActionHandler) host).addSupportedAccessibilityActions(info);
}
// If the request came from keyboard, do not add custom shortcuts as that is already
// exposed as a direct shortcut
if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
@@ -157,14 +162,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
}
private boolean itemSupportsLongClick(View host, ItemInfo info) {
return new CustomActionsPopup(mLauncher, host).canShow()
|| ShortcutUtil.supportsShortcuts(info);
return PopupContainerWithArrow.canShow(host, info)
|| new CustomActionsPopup(mLauncher, host).canShow();
}
private boolean itemSupportsAccessibleDrag(ItemInfo item) {
if (item instanceof WorkspaceItemInfo) {
// Support the action unless the item is in a context menu.
return item.screenId >= 0;
return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION;
}
return (item instanceof LauncherAppWidgetInfo)
|| (item instanceof FolderInfo);
@@ -181,7 +186,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
public boolean performAction(final View host, final ItemInfo item, int action) {
if (action == ACTION_LONG_CLICK) {
if (ShortcutUtil.supportsShortcuts(item)) {
if (PopupContainerWithArrow.canShow(host, item)) {
// Long press should be consumed for workspace items, and it should invoke the
// Shortcuts / Notifications / Actions pop-up menu, and not start a drag as the
// standard long press path does.
@@ -195,7 +200,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
}
}
}
if (host instanceof AccessibilityActionHandler
&& ((AccessibilityActionHandler) host).performAccessibilityAction(action, item)) {
return true;
}
if (action == MOVE) {
beginAccessibleDrag(host, item);
} else if (action == ADD_TO_WORKSPACE) {
@@ -466,4 +474,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
}
return screenId;
}
/**
* An interface allowing views to handle their own action.
*/
public interface AccessibilityActionHandler {
/**
* performs accessibility action and returns true on success
*/
boolean performAccessibilityAction(int action, ItemInfo itemInfo);
/**
* adds all the accessibility actions that can be handled.
*/
void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo);
}
}
@@ -83,7 +83,7 @@ public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
}
@Override
protected boolean isVerticalScrollable() {
return false;
protected boolean canScroll(float absVScroll, float absHScroll) {
return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
}
}
@@ -123,8 +123,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
// Use a light system UI (dark icons) if all apps is behind at least half of the
// status bar.
boolean forceChange = shiftCurrent - mScrimView.getDragHandleSize()
<= mLauncher.getDeviceProfile().getInsets().top / 2;
boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop())
<= mLauncher.getDeviceProfile().getInsets().top / 2f;
if (forceChange) {
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
} else {
@@ -212,9 +212,9 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd);
}
public void setupViews(AllAppsContainerView appsView) {
public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) {
mAppsView = appsView;
mScrimView = mLauncher.findViewById(R.id.scrim_view);
mScrimView = scrimView;
}
/**
@@ -95,9 +95,6 @@ public final class FeatureFlags {
public static final BooleanFlag ENABLE_HINTS_IN_OVERVIEW = getDebugFlag(
"ENABLE_HINTS_IN_OVERVIEW", false, "Show chip hints and gleams on the overview screen");
public static final BooleanFlag FAKE_LANDSCAPE_UI = getDebugFlag(
"FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
"FOLDER_NAME_SUGGEST", true,
"Suggests folder names instead of blank text.");
@@ -17,10 +17,6 @@
package com.android.launcher3.dragndrop;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
import static android.view.View.MeasureSpec.getSize;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
@@ -34,14 +30,12 @@ import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
@@ -52,12 +46,10 @@ import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.Transposable;
import java.util.ArrayList;
@@ -560,145 +552,4 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public OverviewScrim getOverviewScrim() {
return mOverviewScrim;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
RotationMode rotation = mActivity.getRotationMode();
int count = getChildCount();
if (!rotation.isTransposed
|| getMode(widthMeasureSpec) != EXACTLY
|| getMode(heightMeasureSpec) != EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.setRotation(rotation.surfaceRotation);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (!(child instanceof Transposable)) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
} else {
measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0);
child.setPivotX(child.getMeasuredWidth() / 2);
child.setPivotY(child.getMeasuredHeight() / 2);
child.setRotation(rotation.surfaceRotation);
}
}
setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec));
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
RotationMode rotation = mActivity.getRotationMode();
if (!rotation.isTransposed) {
super.onLayout(changed, left, top, right, bottom);
return;
}
final int count = getChildCount();
final int parentWidth = right - left;
final int parentHeight = bottom - top;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
if (lp instanceof LayoutParams) {
final LayoutParams dlp = (LayoutParams) lp;
if (dlp.customPosition) {
child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height);
continue;
}
}
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = Gravity.TOP | Gravity.START;
}
final int layoutDirection = getLayoutDirection();
int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
if (child instanceof Transposable) {
absoluteGravity = rotation.toNaturalGravity(absoluteGravity);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childTop = (parentHeight - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.RIGHT:
childTop = width / 2 + lp.rightMargin - height / 2;
break;
case Gravity.LEFT:
default:
childTop = parentHeight - lp.leftMargin - width / 2 - height / 2;
}
switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.CENTER_VERTICAL:
childLeft = (parentWidth - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.BOTTOM:
childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin;
break;
case Gravity.TOP:
default:
childLeft = height / 2 - width / 2 + lp.topMargin;
}
} else {
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = (parentWidth - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = parentWidth - width - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = lp.leftMargin;
}
switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = (parentHeight - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentHeight - height - lp.bottomMargin;
break;
default:
childTop = lp.topMargin;
}
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
+5 -1
View File
@@ -437,7 +437,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
mItemsInvalidated = true;
mInfo.addListener(this);
mPreviousLabel = mInfo.title.toString();
Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString());
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
if (!isEmpty(mInfo.title)) {
@@ -1648,6 +1648,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
}
public FolderPagedView getContent() {
return mContent;
}
private void logEditFolderLabel() {
LauncherEvent launcherEvent = LauncherEvent.newBuilder()
.setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
@@ -115,6 +115,7 @@ public class FolderAnimationManager {
*/
public AnimatorSet getAnimator() {
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams();
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
@@ -172,7 +172,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
"is dependent on this");
}
DeviceProfile grid = activity.getWallpaperDeviceProfile();
DeviceProfile grid = activity.getDeviceProfile();
FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
.inflate(resId, group, false);
@@ -570,8 +570,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
public void drawDot(Canvas canvas) {
if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
Rect iconBounds = mDotParams.iconBounds;
BubbleTextView.getIconBounds(this, iconBounds,
mActivity.getWallpaperDeviceProfile().iconSizePx);
BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx);
float iconScale = (float) mBackground.previewSize / iconBounds.width();
Utilities.scaleRectAboutCenter(iconBounds, iconScale);
@@ -16,6 +16,9 @@
package com.android.launcher3.folder;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
@@ -27,6 +30,7 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
@@ -258,7 +262,7 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> {
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mPageIndicator.setScroll(l, mMaxScroll);
if (mMaxScroll > 0) mPageIndicator.setScroll(l, mMaxScroll);
}
/**
@@ -614,6 +618,12 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> {
}
}
@Override
protected boolean canScroll(float absVScroll, float absHScroll) {
return AbstractFloatingView.getTopOpenViewWithType(mFolder.mLauncher,
TYPE_ALL & ~TYPE_FOLDER) == null;
}
public int itemsPerPage() {
return mOrganizer.getMaxItemsPerPage();
}
@@ -153,7 +153,7 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0);
ta.recycle();
DeviceProfile grid = activity.getWallpaperDeviceProfile();
DeviceProfile grid = activity.getDeviceProfile();
previewSize = grid.folderIconSizePx;
basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.graphics;
import android.content.Context;
import android.graphics.Rect;
public abstract class RotationMode {
public static final RotationMode NORMAL = new RotationMode(0) { };
public final float surfaceRotation;
public final boolean isTransposed;
public RotationMode(float surfaceRotation) {
this.surfaceRotation = surfaceRotation;
isTransposed = surfaceRotation != 0;
}
public final void mapRect(Rect rect, Rect out) {
mapRect(rect.left, rect.top, rect.right, rect.bottom, out);
}
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.set(left, top, right, bottom);
}
public void mapInsets(Context context, Rect insets, Rect out) {
out.set(insets);
}
public int toNaturalGravity(int absoluteGravity) {
return absoluteGravity;
}
}
@@ -45,8 +45,6 @@ import android.util.LongSparseArray;
import android.util.MutableInt;
import android.util.TimingLogger;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
@@ -282,8 +280,7 @@ public class LoaderTask implements Runnable {
this.notify();
}
@VisibleForTesting
void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI);
}
@@ -121,7 +121,7 @@ public class WorkspacePageIndicator extends View implements Insettable, PageIndi
mLinePaint.setAlpha(0);
mLauncher = Launcher.getLauncher(context);
mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
mLineHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_line_height);
boolean darkText = WallpaperColorInfo.INSTANCE.get(context).supportsDarkText();
mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
@@ -184,6 +184,13 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
return false;
}
/**
* Returns true if we can show the container.
*/
public static boolean canShow(View icon, ItemInfo item) {
return icon instanceof BubbleTextView && ShortcutUtil.supportsShortcuts(item);
}
/**
* Shows the notifications and deep shortcuts associated with {@param icon}.
* @return the container if shown or null.
@@ -196,7 +203,7 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
return null;
}
ItemInfo item = (ItemInfo) icon.getTag();
if (!ShortcutUtil.supportsShortcuts(item)) {
if (!canShow(icon, item)) {
return null;
}
@@ -17,6 +17,7 @@ package com.android.launcher3.states;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Workspace;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -50,8 +51,13 @@ public class HintState extends LauncherState {
@Override
public void onStateTransitionEnd(Launcher launcher) {
launcher.getStateManager().goToState(NORMAL);
LauncherStateManager stateManager = launcher.getStateManager();
Workspace workspace = launcher.getWorkspace();
workspace.post(workspace::moveToDefaultScreen);
boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
stateManager.goToState(NORMAL, true, willMoveScreens ? null
: launcher.getScrimView()::startDragHandleEducationAnim);
if (willMoveScreens) {
workspace.post(workspace::moveToDefaultScreen);
}
}
}
@@ -26,6 +26,7 @@ import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.config.FeatureFlags.FLAG_ENABLE_FIXED_ROTATION_TRANSFORM;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -36,12 +37,9 @@ import android.graphics.RectF;
import android.provider.Settings;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.WindowManager;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.UiThreadHelper;
import java.util.ArrayList;
@@ -77,7 +75,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
private final Launcher mLauncher;
private final Activity mActivity;
private final SharedPreferences mSharedPrefs;
private final SharedPreferences mFeatureFlagsPrefs;
@@ -104,17 +102,16 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
// This is used to defer setting rotation flags until the activity is being created
private boolean mInitialized;
private boolean mDestroyed;
private boolean mRotationHasDifferentUI;
private int mLastActivityFlags = -1;
public RotationHelper(Launcher launcher) {
mLauncher = launcher;
public RotationHelper(Activity activity) {
mActivity = activity;
// On large devices we do not handle auto-rotate differently.
mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation);
mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
if (!mIgnoreAutoRotateSettings) {
mSharedPrefs = Utilities.getPrefs(mLauncher);
mSharedPrefs = Utilities.getPrefs(mActivity);
mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
@@ -122,8 +119,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
mSharedPrefs = null;
}
mContentResolver = launcher.getContentResolver();
mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mLauncher);
mContentResolver = activity.getContentResolver();
mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mActivity);
mFeatureFlagsPrefs.registerOnSharedPreferenceChangeListener(this);
updateForcedRotation(true);
}
@@ -144,7 +141,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
mForcedRotation = isForcedRotation;
}
UI_HELPER_EXECUTOR.execute(() -> {
if (mLauncher.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) {
if (mActivity.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) {
Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME,
mForcedRotation ? 1 : 0);
}
@@ -165,29 +162,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
mForcedRotationChangedListeners.remove(listener);
}
public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) {
mRotationHasDifferentUI = rotationHasDifferentUI;
}
public boolean homeScreenCanRotate() {
return mRotationHasDifferentUI || mIgnoreAutoRotateSettings || mAutoRotateEnabled
|| mStateHandlerRequest != REQUEST_NONE
|| mLauncher.getDeviceProfile().isMultiWindowMode;
}
public void updateRotationAnimation() {
if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes();
int oldAnim = lp.rotationAnimation;
lp.rotationAnimation = homeScreenCanRotate()
? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE
: WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
if (oldAnim != lp.rotationAnimation) {
mLauncher.getWindow().setAttributes(lp);
}
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
if (FLAG_ENABLE_FIXED_ROTATION_TRANSFORM.equals(s)) {
@@ -199,17 +173,13 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
if (mAutoRotateEnabled != wasRotationEnabled) {
notifyChange();
updateRotationAnimation();
mLauncher.reapplyUi();
}
}
public void setStateHandlerRequest(int request) {
if (mStateHandlerRequest != request) {
mStateHandlerRequest = request;
updateRotationAnimation();
notifyChange();
}
}
@@ -231,7 +201,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
// Used by tests only.
public void forceAllowRotationForTesting(boolean allowRotation) {
mIgnoreAutoRotateSettings =
allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation);
allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation);
// TODO(b/150214193) Tests currently expect launcher to be able to be rotated
// Modify tests for this new behavior
mForcedRotation = !allowRotation;
@@ -243,7 +213,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
if (!mInitialized) {
mInitialized = true;
notifyChange();
updateRotationAnimation();
}
}
@@ -285,7 +254,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
}
if (activityFlags != mLastActivityFlags) {
mLastActivityFlags = activityFlags;
UiThreadHelper.setOrientationAsync(mLauncher, activityFlags);
UiThreadHelper.setOrientationAsync(mActivity, activityFlags);
}
}
@@ -184,6 +184,10 @@ public class TestInformationHandler implements ResourceBasedOverride {
mDeviceProfile.allAppsCellHeightPx);
break;
}
case TestProtocol.REQUEST_MOCK_SENSOR_ROTATION:
TestProtocol.sDisableSensorRotation = true;
break;
}
return response;
}
@@ -92,6 +92,9 @@ public final class TestProtocol {
public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled";
public static boolean sDisableSensorRotation;
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
@@ -16,8 +16,11 @@
package com.android.launcher3.touch;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -32,13 +35,8 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.OverScroller;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
public class LandscapePagedViewHandler implements PagedOrientationHandler {
@Override
@@ -67,12 +65,11 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
}
@Override
public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) {
int scroll = pagedView.getScrollY();
final int halfPageSize = pagedView.getNormalChildHeight() / 2;
final int screenCenter = mInsets.top + pagedView.getPaddingTop() + scroll + halfPageSize;
final int halfScreenSize = pagedView.getMeasuredHeight() / 2;
return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize);
public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) {
out.scroll = view.getScrollY();
out.halfPageSize = view.getNormalChildHeight() / 2;
out.halfScreenSize = view.getMeasuredHeight() / 2;
out.screenCenter = mInsets.top + view.getPaddingTop() + out.scroll + out.halfPageSize;
}
@Override
@@ -83,7 +83,7 @@ public interface PagedOrientationHandler {
void delegateScrollTo(PagedView pagedView, int primaryScroll);
void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y);
void scrollerStartScroll(OverScroller scroller, int newPosition);
CurveProperties getCurveProperties(PagedView pagedView, Rect insets);
void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out);
boolean isGoingUp(float displacement);
/**
@@ -92,18 +92,12 @@ public interface PagedOrientationHandler {
*/
void adjustFloatingIconStartVelocity(PointF velocity);
class CurveProperties {
public final int scroll;
public final int halfPageSize;
public final int screenCenter;
public final int halfScreenSize;
public CurveProperties(int scroll, int halfPageSize, int screenCenter, int halfScreenSize) {
this.scroll = scroll;
this.halfPageSize = halfPageSize;
this.screenCenter = screenCenter;
this.halfScreenSize = halfScreenSize;
}
class CurveProperties {
public int scroll;
public int halfPageSize;
public int screenCenter;
public int halfScreenSize;
}
class ChildBounds {
@@ -16,8 +16,11 @@
package com.android.launcher3.touch;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -32,13 +35,8 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.OverScroller;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
public class PortraitPagedViewHandler implements PagedOrientationHandler {
@Override
@@ -67,12 +65,11 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
}
@Override
public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) {
int scroll = pagedView.getScrollX();
final int halfPageSize = pagedView.getNormalChildWidth() / 2;
final int screenCenter = mInsets.left + pagedView.getPaddingLeft() + scroll + halfPageSize;
final int halfScreenSize = pagedView.getMeasuredWidth() / 2;
return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize);
public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) {
out.scroll = view.getScrollX();
out.halfPageSize = view.getNormalChildWidth() / 2;
out.halfScreenSize = view.getMeasuredWidth() / 2;
out.screenCenter = mInsets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize;
}
@Override
@@ -22,7 +22,6 @@ import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.dot.DotInfo;
/**
@@ -57,19 +56,6 @@ public interface ActivityContext {
DeviceProfile getDeviceProfile();
/**
* Device profile to be used by UI elements which are shown directly on top of the wallpaper
* and whose presentation is tied to the wallpaper (and physical device) and not the activity
* configuration.
*/
default DeviceProfile getWallpaperDeviceProfile() {
return getDeviceProfile();
}
default RotationMode getRotationMode() {
return RotationMode.NORMAL;
}
static ActivityContext lookupContext(Context context) {
if (context instanceof ActivityContext) {
return (ActivityContext) context;
@@ -92,8 +92,6 @@ public class FloatingIconView extends FrameLayout implements
private ClipIconView mClipIconView;
private @Nullable Drawable mBadge;
private float mRotation;
private View mOriginalIcon;
private RectF mPositionOut;
private Runnable mOnTargetChangeRunnable;
@@ -194,18 +192,17 @@ public class FloatingIconView extends FrameLayout implements
* @param positionOut Rect that will hold the size and position of v.
*/
private void matchPositionOf(Launcher launcher, View v, boolean isOpening, RectF positionOut) {
float rotation = getLocationBoundsForView(launcher, v, isOpening, positionOut);
getLocationBoundsForView(launcher, v, isOpening, positionOut);
final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
Math.round(positionOut.width()),
Math.round(positionOut.height()));
updatePosition(rotation, positionOut, lp);
updatePosition(positionOut, lp);
setLayoutParams(lp);
mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
}
private void updatePosition(float rotation, RectF pos, InsettableFrameLayout.LayoutParams lp) {
mRotation = rotation;
private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
mPositionOut.set(pos);
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
@@ -228,7 +225,7 @@ public class FloatingIconView extends FrameLayout implements
* - For DeepShortcutView, we return the bounds of the icon view.
* - For BubbleTextView, we return the icon bounds.
*/
private static float getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
RectF outRect) {
boolean ignoreTransform = !isOpening;
if (v instanceof DeepShortcutView) {
@@ -239,7 +236,7 @@ public class FloatingIconView extends FrameLayout implements
ignoreTransform = false;
}
if (v == null) {
return 0;
return;
}
Rect iconBounds = new Rect();
@@ -253,15 +250,13 @@ public class FloatingIconView extends FrameLayout implements
float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right,
iconBounds.bottom};
float[] rotation = new float[] {0};
Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
false, ignoreTransform, rotation);
false, ignoreTransform);
outRect.set(
Math.min(points[0], points[2]),
Math.min(points[1], points[3]),
Math.max(points[0], points[2]),
Math.max(points[1], points[3]));
return rotation[0];
}
/**
@@ -443,14 +438,10 @@ public class FloatingIconView extends FrameLayout implements
@Override
protected void dispatchDraw(Canvas canvas) {
int count = canvas.save();
canvas.rotate(mRotation,
mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY());
super.dispatchDraw(canvas);
if (mBadge != null) {
mBadge.draw(canvas);
}
canvas.restoreToCount(count);
}
public void fastFinish() {
@@ -487,11 +478,10 @@ public class FloatingIconView extends FrameLayout implements
@Override
public void onGlobalLayout() {
if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
float rotation = getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
sTmpRectF);
if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) {
updatePosition(rotation, sTmpRectF,
(InsettableFrameLayout.LayoutParams) getLayoutParams());
if (!sTmpRectF.equals(mPositionOut)) {
updatePosition(sTmpRectF, (InsettableFrameLayout.LayoutParams) getLayoutParams());
if (mOnTargetChangeRunnable != null) {
mOnTargetChangeRunnable.run();
}
+108 -54
View File
@@ -22,8 +22,10 @@ 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.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.Animator;
@@ -33,8 +35,10 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.RectEvaluator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -98,6 +102,12 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
private static final int SETTINGS = R.string.settings_button_text;
private static final int ALPHA_CHANNEL_COUNT = 1;
private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300;
// How much to delay before repeating the bounce.
private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200;
// Repeat this many times (i.e. total number of bounces is 1 + this).
private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2;
private final Rect mTempRect = new Rect();
private final int[] mTempPos = new int[2];
@@ -115,10 +125,13 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
protected int mEndFlatColor;
protected int mEndFlatColorAlpha;
protected final int mDragHandleSize;
protected final Point mDragHandleSize;
private final int mDragHandleTouchSize;
private final int mDragHandlePaddingInVerticalBarLayout;
protected float mDragHandleOffset;
private final Rect mDragHandleBounds;
private final RectF mHitRect = new RectF();
private ObjectAnimator mDragHandleAnim;
private final MultiValueAlpha mMultiValueAlpha;
@@ -136,9 +149,13 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
mMaxScrimAlpha = 0.7f;
mDragHandleSize = context.getResources()
.getDimensionPixelSize(R.dimen.vertical_drag_handle_size);
mDragHandleBounds = new Rect(0, 0, mDragHandleSize, mDragHandleSize);
Resources res = context.getResources();
mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width),
res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height));
mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y);
mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size);
mDragHandlePaddingInVerticalBarLayout = context.getResources()
.getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout);
mAccessibilityHelper = createAccessibilityHelper();
ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
@@ -204,6 +221,7 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
public void setProgress(float progress) {
if (mProgress != progress) {
mProgress = progress;
stopDragHandleEducationAnim();
updateColors();
updateDragHandleAlpha();
invalidate();
@@ -251,70 +269,103 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean value = super.onTouchEvent(event);
if (!value && mDragHandle != null && event.getAction() == ACTION_DOWN
&& mDragHandle.getAlpha() == 255
&& mHitRect.contains(event.getX(), event.getY())) {
final Drawable drawable = mDragHandle;
mDragHandle = null;
Rect bounds = new Rect(mDragHandleBounds);
bounds.offset(0, -(int) mDragHandleOffset);
drawable.setBounds(bounds);
Rect topBounds = new Rect(bounds);
topBounds.offset(0, -bounds.height() / 2);
Rect invalidateRegion = new Rect(bounds);
invalidateRegion.top = topBounds.top;
Keyframe frameTop = Keyframe.ofObject(0.6f, topBounds);
frameTop.setInterpolator(DEACCEL);
Keyframe frameBot = Keyframe.ofObject(1, bounds);
frameBot.setInterpolator(ACCEL);
PropertyValuesHolder holder = PropertyValuesHolder .ofKeyframe("bounds",
Keyframe.ofObject(0, bounds), frameTop, frameBot);
holder.setEvaluator(new RectEvaluator());
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
getOverlay().remove(drawable);
updateDragHandleVisibility(drawable);
boolean superHandledTouch = super.onTouchEvent(event);
if (event.getAction() == ACTION_DOWN) {
if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) {
if (startDragHandleEducationAnim()) {
return true;
}
});
anim.addUpdateListener((v) -> invalidate(invalidateRegion));
getOverlay().add(drawable);
anim.start();
return true;
}
stopDragHandleEducationAnim();
}
return superHandledTouch;
}
/**
* Animates the drag handle to demonstrate how to get to all apps.
* @return Whether the animation was started (false if drag handle is invisible).
*/
public boolean startDragHandleEducationAnim() {
stopDragHandleEducationAnim();
if (mDragHandle == null || mDragHandle.getAlpha() != 255) {
return false;
}
final Drawable drawable = mDragHandle;
mDragHandle = null;
Rect bounds = new Rect(mDragHandleBounds);
bounds.offset(0, -(int) mDragHandleOffset);
drawable.setBounds(bounds);
Rect topBounds = new Rect(bounds);
topBounds.offset(0, -bounds.height());
Rect invalidateRegion = new Rect(bounds);
invalidateRegion.top = topBounds.top;
final float progressToReachTop = 0.6f;
Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds);
frameTop.setInterpolator(DEACCEL);
Keyframe frameBot = Keyframe.ofObject(1, bounds);
frameBot.setInterpolator(ACCEL_DEACCEL);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds",
Keyframe.ofObject(0, bounds), frameTop, frameBot);
holder.setEvaluator(new RectEvaluator());
mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS;
// The bounce finishes by this progress, the rest of the duration just delays next bounce.
float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration;
mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion));
mDragHandleAnim.setDuration(totalBounceDuration);
mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress));
mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT);
getOverlay().add(drawable);
mDragHandleAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mDragHandleAnim = null;
getOverlay().remove(drawable);
updateDragHandleVisibility(drawable);
}
});
mDragHandleAnim.start();
return true;
}
private void stopDragHandleEducationAnim() {
if (mDragHandleAnim != null) {
mDragHandleAnim.end();
}
return value;
}
protected void updateDragHandleBounds() {
DeviceProfile grid = mLauncher.getDeviceProfile();
final int left;
final int width = getMeasuredWidth();
final int top = getMeasuredHeight() - mDragHandleSize - grid.getInsets().bottom;
final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom;
final int topMargin;
if (grid.isVerticalBarLayout()) {
topMargin = grid.workspacePadding.bottom;
topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout;
if (grid.isSeascape()) {
left = width - grid.getInsets().right - mDragHandleSize;
left = width - grid.getInsets().right - mDragHandleSize.x
- mDragHandlePaddingInVerticalBarLayout;
} else {
left = mDragHandleSize + grid.getInsets().left;
left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout;
}
} else {
left = (width - mDragHandleSize) / 2;
left = Math.round((width - mDragHandleSize.x) / 2f);
topMargin = grid.hotseatBarSizePx;
}
mDragHandleBounds.offsetTo(left, top - topMargin);
mHitRect.set(mDragHandleBounds);
float inset = -mDragHandleSize / 2;
mHitRect.inset(inset, inset);
// Inset outwards to increase touch size.
mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f,
(mDragHandleSize.y - mDragHandleTouchSize) / 2f);
if (mDragHandle != null) {
mDragHandle.setBounds(mDragHandleBounds);
@@ -341,7 +392,7 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
if (visible != wasVisible) {
if (visible) {
mDragHandle = recycle != null ? recycle :
mLauncher.getDrawable(R.drawable.drag_handle_indicator);
mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow);
mDragHandle.setBounds(mDragHandleBounds);
updateDragHandleAlpha();
@@ -397,7 +448,7 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
@Override
protected int getVirtualViewAt(float x, float y) {
return mDragHandleBounds.contains((int) x, (int) y)
return mHitRect.contains((int) x, (int) y)
? DRAG_HANDLE_ID : INVALID_ID;
}
@@ -470,7 +521,10 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
}
}
public int getDragHandleSize() {
return mDragHandleSize;
/**
* @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top.
*/
public float getVisualTop() {
return Float.MAX_VALUE;
}
}
@@ -1,26 +0,0 @@
/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.views;
import com.android.launcher3.graphics.RotationMode;
/**
* Indicates that a view can be transposed.
*/
public interface Transposable {
RotationMode getRotationMode();
}
@@ -98,6 +98,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet
onWidgetsBound();
}
@VisibleForTesting
public WidgetsRecyclerView getRecyclerView() {
return mRecyclerView;
}
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(mRecyclerView, getContext().getString(
@@ -19,11 +19,14 @@ package com.android.launcher3.widget;
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -155,18 +158,66 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
}
if (mTouchDownOnScroller) {
return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 1 " + result);
}
return result;
}
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 2 false");
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView.onTouchEvent");
}
if (mTouchDownOnScroller) {
mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
}
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onRequestDisallowInterceptTouchEvent "
+ disallowIntercept);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean result = super.dispatchTouchEvent(ev);
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state: "
+ getScrollState()
+ " can scroll: " + getLayoutManager().canScrollVertically()
+ " result: " + result
+ " layout suppressed: " + isLayoutSuppressed()
+ " event: " + ev);
}
return result;
}
@Override
public void stopNestedScroll() {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "stopNestedScroll");
}
super.stopNestedScroll();
}
@Override
public void setLayoutFrozen(boolean frozen) {
if (frozen != isLayoutSuppressed()) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "setLayoutFrozen " + frozen
+ " @ " + android.util.Log.getStackTraceString(new Throwable()));
}
}
super.setLayoutFrozen(frozen);
}
}
@@ -162,6 +162,7 @@ public abstract class AbstractLauncherUiTest {
mLauncher.enableDebugTracing();
// Avoid double-reporting of Launcher crashes.
mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0);
mLauncher.disableSensorRotation();
}
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@@ -277,6 +278,7 @@ public abstract class AbstractLauncherUiTest {
clearPackageData(mDevice.getLauncherPackageName());
mLauncher.enableDebugTracing();
mLauncherPid = mLauncher.getPid();
mLauncher.disableSensorRotation();
}
}
@@ -275,8 +275,10 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
}
private void verifyPendingWidgetPresent() {
final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT);
if (widget == null) mLauncher.dumpViewHierarchy(); // b/152645831
assertTrue("Pending widget is not present",
mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null);
widget != null);
}
/**
@@ -43,7 +43,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
AllApps(LauncherInstrumentation launcher) {
super(launcher);
final UiObject2 allAppsContainer = verifyActiveContainer();
mHeight = allAppsContainer.getVisibleBounds().height();
mHeight = mLauncher.getVisibleBounds(allAppsContainer).height();
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
// Wait for the recycler to populate.
@@ -66,7 +66,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
LauncherInstrumentation.log("hasClickableIcon: icon not visible");
return false;
}
final Rect iconBounds = icon.getVisibleBounds();
final Rect iconBounds = mLauncher.getVisibleBounds(icon);
LauncherInstrumentation.log("hasClickableIcon: icon bounds: " + iconBounds);
if (iconBounds.height() < mIconHeight / 2) {
LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height");
@@ -86,7 +86,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
private boolean iconCenterInSearchBox(UiObject2 allAppsContainer, UiObject2 icon) {
final Point iconCenter = icon.getVisibleCenter();
return getSearchBox(allAppsContainer).getVisibleBounds().contains(
return mLauncher.getVisibleBounds(getSearchBox(allAppsContainer)).contains(
iconCenter.x, iconCenter.y);
}
@@ -125,11 +125,11 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
mLauncher.getObjectsInContainer(allAppsContainer, "icon")
.stream()
.filter(icon ->
icon.getVisibleBounds().bottom
mLauncher.getVisibleBounds(icon).bottom
<= displayBottom)
.collect(Collectors.toList()),
searchBox.getVisibleBounds().bottom
- allAppsContainer.getVisibleBounds().top);
mLauncher.getVisibleBounds(searchBox).bottom
- mLauncher.getVisibleBounds(allAppsContainer).top);
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
"Scrolled in a wrong direction in AllApps: from " + scroll + " to "
@@ -166,7 +166,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
final UiObject2 searchBox = getSearchBox(allAppsContainer);
int attempts = 0;
final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
final Rect margins =
new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5);
for (int scroll = getAllAppsScroll();
scroll != 0;
@@ -117,8 +117,8 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
// part) one in the center, and parts of its right and left siblings. Find the
// main task view by its width.
final UiObject2 widestTask = Collections.max(taskViews,
(t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
t2.getVisibleBounds().width()));
(t1, t2) -> Integer.compare(mLauncher.getVisibleBounds(t1).width(),
mLauncher.getVisibleBounds(t2).width()));
return new OverviewTask(mLauncher, widestTask, this);
}
@@ -57,7 +57,7 @@ abstract class Launchable {
private Background launch(BySelector selector) {
LauncherInstrumentation.log("Launchable.launch before click " +
mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
mLauncher.executeAndWaitForEvent(
() -> mLauncher.clickLauncherObject(mObject),
@@ -977,7 +977,7 @@ public final class LauncherInstrumentation {
int getBottomGestureMarginInContainer(UiObject2 container) {
final int bottomGestureStartOnScreen = getRealDisplaySize().y - getBottomGestureSize();
return container.getVisibleBounds().bottom - bottomGestureStartOnScreen;
return getVisibleBounds(container).bottom - bottomGestureStartOnScreen;
}
void clickLauncherObject(UiObject2 object) {
@@ -995,10 +995,10 @@ public final class LauncherInstrumentation {
Collection<UiObject2> items,
int topPaddingInContainer) {
final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
Integer.compare(i1.getVisibleBounds().top, i2.getVisibleBounds().top));
Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top));
final int itemRowCurrentTopOnScreen = lowestItem.getVisibleBounds().top;
final Rect containerRect = container.getVisibleBounds();
final int itemRowCurrentTopOnScreen = getVisibleBounds(lowestItem).top;
final Rect containerRect = getVisibleBounds(container);
final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer;
final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop();
@@ -1017,7 +1017,7 @@ public final class LauncherInstrumentation {
void scroll(
UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) {
final Rect rect = container.getVisibleBounds();
final Rect rect = getVisibleBounds(container);
if (margins != null) {
rect.left += margins.left;
rect.top += margins.top;
@@ -1263,6 +1263,10 @@ public final class LauncherInstrumentation {
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
public void disableSensorRotation() {
getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION);
}
public void disableDebugTracing() {
getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
@@ -1330,4 +1334,13 @@ public final class LauncherInstrumentation {
void expectEvent(String sequence, Pattern expected) {
if (sCheckingEvents) sEventChecker.expectPattern(sequence, expected);
}
Rect getVisibleBounds(UiObject2 object) {
try {
return object.getVisibleBounds();
} catch (Throwable t) {
fail(t.toString());
return null;
}
}
}
@@ -41,7 +41,7 @@ public class OptionsPopupMenuItem {
public void launch(@NonNull String expectedPackageName) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
LauncherInstrumentation.log("OptionsPopupMenuItem before click "
+ mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
+ mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
mLauncher.clickLauncherObject(mObject);
if (!Build.MODEL.contains("Cuttlefish") ||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q &&
@@ -56,7 +56,7 @@ public final class OverviewTask {
"want to dismiss a task")) {
verifyActiveContainer();
// Dismiss the task via flinging it up.
final Rect taskBounds = mTask.getVisibleBounds();
final Rect taskBounds = mLauncher.getVisibleBounds(mTask);
final int centerX = taskBounds.centerX();
final int centerY = taskBounds.centerY();
mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false,
@@ -73,7 +73,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
mLauncher.scroll(
widgetsContainer,
Direction.UP,
new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0),
new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0),
FLING_STEPS, false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();
@@ -111,19 +111,19 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
int maxWidth = 0;
for (UiObject2 sibling : widget.getParent().getChildren()) {
maxWidth = Math.max(sibling.getVisibleBounds().width(), maxWidth);
maxWidth = Math.max(mLauncher.getVisibleBounds(sibling).width(), maxWidth);
}
int visibleDelta = maxWidth - widget.getVisibleBounds().width();
int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width();
if (visibleDelta > 0) {
Rect parentBounds = cell.getVisibleBounds();
Rect parentBounds = mLauncher.getVisibleBounds(cell);
mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
+ mLauncher.getTouchSlop(),
parentBounds.centerY(), parentBounds.centerX(),
parentBounds.centerY(), 10, true, GestureScope.INSIDE);
}
if (widget.getVisibleBounds().bottom
if (mLauncher.getVisibleBounds(widget).bottom
<= displaySize.y - mLauncher.getBottomGestureSize()) {
return new Widget(mLauncher, widget);
}
@@ -177,7 +177,7 @@ public final class Workspace extends Home {
mLauncher,
getHotseatAppIcon("Chrome"),
new Point(mLauncher.getDevice().getDisplayWidth(),
workspace.getVisibleBounds().centerY()),
mLauncher.getVisibleBounds(workspace).centerY()),
"deep_shortcuts_container",
false,
() -> mLauncher.expectEvent(