Merge "Fixing animation player not overshooting spring animation; tuning springs" into ub-launcher3-rvc-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 6a3466c..d22e5af 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -334,7 +334,7 @@
mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
}
int displayRotation = 0;
- if (mOrientedState != null && !mOrientedState.areMultipleLayoutOrientationsDisabled()) {
+ if (mOrientedState != null && mOrientedState.isMultipleOrientationSupportedByDevice()) {
// TODO(b/150300347): The first recents animation after launcher is started with the
// foreground app not in landscape will look funky until that bug is fixed
displayRotation = mOrientedState.getDisplayRotation();
@@ -472,6 +472,7 @@
FloatingIconView fiv = (FloatingIconView) floatingView;
anim.addAnimatorListener(fiv);
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ fiv.setFastFinishRunnable(anim::end);
}
AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 5a64382..bf01780 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -208,7 +208,8 @@
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
- mTaskViewSimulator = new TaskViewSimulator(context, LayoutUtils::calculateLauncherTaskSize);
+ mTaskViewSimulator = new TaskViewSimulator(
+ context, LayoutUtils::calculateLauncherTaskSize, true);
initAfterSubclassConstructor();
initStateCallbacks();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 9b5a935..28c2b97 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
+import static com.android.quickstep.util.RecentsOrientedState.isFixedRotationTransformEnabled;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -597,7 +598,7 @@
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
- if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
+ if (!isFixedRotationTransformEnabled(this)) {
return;
}
mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 235ac16..3cf9b2c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -65,7 +65,7 @@
}
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ super(context, attrs, defStyleAttr, false);
}
@Override
@@ -195,9 +195,4 @@
}
super.applyLoadPlan(tasks);
}
-
- @Override
- protected boolean supportsVerticalLandscape() {
- return false;
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 00329b8..9309110 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -267,7 +267,8 @@
mTmpRectF.set(mTargetRect);
Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale);
mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF));
- if (mOrientedState == null || mOrientedState.areMultipleLayoutOrientationsDisabled()) {
+ if (mOrientedState == null
+ || !mOrientedState.isMultipleOrientationSupportedByDevice()) {
mCurrentRect.offset(params.mOffset, 0);
} else {
int displayRotation = mOrientedState.getDisplayRotation();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
index dde7605..8a6c4a1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -207,9 +207,23 @@
mRectScaleAnim.skipToEnd();
}
}
+ mRectXAnimEnded = true;
+ mRectYAnimEnded = true;
+ mRectScaleAnimEnded = true;
+ maybeOnEnd();
+ }
+
+ private boolean isEnded() {
+ return mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded;
}
private void onUpdate() {
+ if (isEnded()) {
+ // Prevent further updates from being called. This can happen between callbacks for
+ // ending the x/y/scale animations.
+ return;
+ }
+
if (!mOnUpdateListeners.isEmpty()) {
float currentWidth = Utilities.mapRange(mCurrentScaleProgress, mStartRect.width(),
mTargetRect.width());
@@ -229,7 +243,7 @@
}
private void maybeOnEnd() {
- if (mAnimsStarted && mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded) {
+ if (mAnimsStarted && isEnded()) {
mAnimsStarted = false;
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
animatorListener.onAnimationEnd(null);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 0131fdf..0bc021b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,9 +15,13 @@
*/
package com.android.quickstep.util;
+import static android.view.Surface.ROTATION_0;
+
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.AppWindowAnimationHelper.applySurfaceParams;
+import static com.android.quickstep.util.RecentsOrientedState.isFixedRotationTransformEnabled;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -33,7 +37,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationTargets;
@@ -91,11 +94,17 @@
private boolean mLayoutValid = false;
private boolean mScrollValid = false;
- public TaskViewSimulator(Context context, TaskSizeProvider sizeProvider) {
+ public TaskViewSimulator(Context context, TaskSizeProvider sizeProvider,
+ boolean rotationSupportedByActivity) {
mContext = context;
mSizeProvider = sizeProvider;
mPositionHelper = new PreviewPositionHelper(context);
- mOrientationState = new RecentsOrientedState(context);
+
+ mOrientationState = new RecentsOrientedState(context, rotationSupportedByActivity,
+ i -> { });
+ // We do not need to attach listeners as the simulator is created just for the gesture
+ // duration, and any settings are unlikely to change during this
+ mOrientationState.initWithoutListeners();
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
@@ -114,11 +123,15 @@
* @see com.android.quickstep.views.RecentsView#setLayoutRotation(int, int)
*/
public void setLayoutRotation(int touchRotation, int displayRotation) {
- if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
- return;
+ int launcherRotation;
+ if (!mOrientationState.isMultipleOrientationSupportedByDevice()
+ || mOrientationState.isHomeRotationAllowed()) {
+ launcherRotation = displayRotation;
+ } else {
+ launcherRotation = ROTATION_0;
}
- mOrientationState.update(touchRotation, displayRotation,
- mOrientationState.getLauncherRotation());
+
+ mOrientationState.update(touchRotation, displayRotation, launcherRotation);
mLayoutValid = false;
}
@@ -180,7 +193,7 @@
mLayoutValid = true;
getFullScreenScale();
- mThumbnailData.rotation = FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()
+ mThumbnailData.rotation = isFixedRotationTransformEnabled(mContext)
? mOrientationState.getDisplayRotation() : mPositionHelper.getCurrentRotation();
mPositionHelper.updateThumbnailMatrix(mThumbnailPosition, mThumbnailData,
@@ -226,7 +239,8 @@
// Apply recensView matrix
mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
- postDisplayRotation(mOrientationState.getDisplayRotation(),
+ postDisplayRotation(deltaRotation(
+ mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
mDp.widthPx, mDp.heightPx, mMatrix);
// Crop rect is the inverse of thumbnail matrix
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 454223e..0b6d340 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -45,7 +45,6 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
@@ -96,7 +95,7 @@
}
public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ super(context, attrs, defStyleAttr, true);
mActivity.getStateManager().addStateListener(this);
}
@@ -265,12 +264,6 @@
}
@Override
- protected boolean supportsVerticalLandscape() {
- return FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()
- && !mOrientationState.areMultipleLayoutOrientationsDisabled();
- }
-
- @Override
public void reset() {
super.reset();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index 93e68c0..d160686 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -48,7 +48,8 @@
HIDDEN_NON_ZERO_ROTATION,
HIDDEN_NO_TASKS,
HIDDEN_GESTURE_RUNNING,
- HIDDEN_NO_RECENTS})
+ HIDDEN_NO_RECENTS,
+ HIDDEN_FULLESCREEN_PROGRESS})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsHiddenFlags { }
@@ -58,6 +59,7 @@
public static final int HIDDEN_NO_TASKS = 1 << 3;
public static final int HIDDEN_GESTURE_RUNNING = 1 << 4;
public static final int HIDDEN_NO_RECENTS = 1 << 5;
+ public static final int HIDDEN_FULLESCREEN_PROGRESS = 1 << 6;
private static final int INDEX_CONTENT_ALPHA = 0;
private static final int INDEX_VISIBILITY_ALPHA = 1;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index eee35d1..90dc5a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -38,6 +38,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.views.OverviewActionsView.HIDDEN_FULLESCREEN_PROGRESS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_GESTURE_RUNNING;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
@@ -75,8 +76,6 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.OrientationEventListener;
-import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -103,7 +102,6 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -191,8 +189,6 @@
};
protected final RecentsOrientedState mOrientationState;
- private OrientationEventListener mOrientationListener;
- private int mPreviousRotation;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected AppWindowAnimationHelper mAppWindowAnimationHelper;
@@ -301,9 +297,6 @@
}
};
- private final RecentsOrientedState.SystemRotationChangeListener mSystemRotationChangeListener =
- enabled -> toggleOrientationEventListener();
-
private final PinnedStackAnimationListener mIPinnedStackAnimationListener =
new PinnedStackAnimationListener();
@@ -360,11 +353,13 @@
}
};
- public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
+ boolean rotationSupportedByActivity) {
super(context, attrs, defStyleAttr);
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
setEnableFreeScroll(true);
- mOrientationState = new RecentsOrientedState(context);
+ mOrientationState = new RecentsOrientedState(
+ context, rotationSupportedByActivity, this::animateRecentsRotationInPlace);
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
@@ -399,21 +394,10 @@
.getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
setWillNotDraw(false);
updateEmptyMessage();
- disableMultipleLayoutRotations(!supportsVerticalLandscape());
+ mOrientationHandler = mOrientationState.getOrientationHandler();
// 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 = RecentsOrientedState.getRotationForUserDegreesRotated(i);
- if (mPreviousRotation != rotation) {
- animateRecentsRotationInPlace(rotation);
- mPreviousRotation = rotation;
- }
- }
- };
}
public OverScroller getScroller() {
@@ -502,7 +486,6 @@
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
mIPinnedStackAnimationListener);
mOrientationState.init();
- mOrientationState.addSystemRotationChangeListener(mSystemRotationChangeListener);
}
@Override
@@ -517,7 +500,6 @@
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
mIPinnedStackAnimationListener.setActivity(null);
- mOrientationState.removeSystemRotationChangeListener(mSystemRotationChangeListener);
mOrientationState.destroy();
}
@@ -576,31 +558,12 @@
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
+ mOrientationState.setRotationWatcherEnabled(enabled);
if (!enabled) {
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTask = null;
}
- toggleOrientationEventListener();
- }
-
- private void toggleOrientationEventListener() {
- boolean canEnable = canEnableOverviewRotationAnimation() && mOverviewStateEnabled;
- UI_HELPER_EXECUTOR.execute(() -> {
- if (canEnable) {
- mOrientationListener.enable();
- } else {
- mOrientationListener.disable();
- }
- });
- }
-
- private boolean canEnableOverviewRotationAnimation() {
- return supportsVerticalLandscape() // not 3P launcher
- && !TestProtocol.sDisableSensorRotation // Ignore hardware dependency for tests..
- && mOrientationListener.canDetectOrientation() // ..but does the hardware even work?
- && (mOrientationState.isSystemRotationAllowed() &&
- !mOrientationState.canLauncherRotate()); // launcher is going to rotate itself
}
public void onDigitalWellbeingToastShown() {
@@ -820,9 +783,7 @@
for (int i = 0; i < taskCount; i++) {
getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
}
- if (mActionsView != null && mOrientationState.getLauncherRotation() == Surface.ROTATION_0) {
- mActionsView.setVisibility(fullscreenProgress == 0 ? VISIBLE : INVISIBLE);
- }
+ mActionsView.updateHiddenFlags(HIDDEN_FULLESCREEN_PROGRESS, fullscreenProgress > 0);
}
private void updateTaskStackListenerState() {
@@ -1039,10 +1000,6 @@
}
private void animateRecentsRotationInPlace(int newRotation) {
- if (!supportsVerticalLandscape()) {
- return;
- }
-
AnimatorSet pa = setRecentsChangedOrientation(true);
pa.addListener(AnimationSuccessListener.forRunnable(() -> {
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
@@ -1067,7 +1024,6 @@
return as;
}
- abstract protected boolean supportsVerticalLandscape();
private void rotateAllChildTasks() {
for (int i = 0; i < getTaskViewCount(); i++) {
@@ -1098,7 +1054,7 @@
*/
public void showCurrentTask(int runningTaskId) {
if (getTaskView(runningTaskId) == null) {
- boolean wasEmpty = getTaskViewCount() == 0;
+ boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
addView(taskView, mTaskViewStartIndex);
@@ -1611,17 +1567,12 @@
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers();
- mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, touchRotation != 0);
+ mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
+ touchRotation != 0 || launcherRotation != 0);
requestLayout();
}
}
- public void disableMultipleLayoutRotations(boolean disable) {
- mOrientationState.disableMultipleOrientations(disable);
- mOrientationHandler = mOrientationState.getOrientationHandler();
- requestLayout();
- }
-
public RecentsOrientedState getPagedViewOrientedState() {
return mOrientationState;
}
@@ -2083,8 +2034,7 @@
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
float degreesRotated;
if (navbarRotation == 0) {
- degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 :
- mOrientationHandler.getDegreesRotated();
+ degreesRotated = mOrientationHandler.getDegreesRotated();
} else {
degreesRotated = -navbarRotation;
}
@@ -2097,7 +2047,7 @@
// PagedOrientationHandler
return e -> {
if (navbarRotation != 0
- && !mOrientationState.areMultipleLayoutOrientationsDisabled()
+ && mOrientationState.isMultipleOrientationSupportedByDevice()
&& !mOrientationState.getOrientationHandler().isLayoutNaturalToLauncher()) {
mOrientationState.flipVertical(e);
super.onTouchEvent(e);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 0a00a61..a6ce2b5 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -21,6 +21,7 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+import static com.android.quickstep.util.RecentsOrientedState.isFixedRotationTransformEnabled;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
@@ -35,7 +36,6 @@
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -51,8 +51,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.NavBarPosition;
@@ -176,7 +174,7 @@
}
private void setupOrientationSwipeHandler() {
- if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
+ if (!isFixedRotationTransformEnabled(mContext)) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index 2a0fe32..dfb8c1d 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -28,7 +28,7 @@
public class ActivityInitListener<T extends BaseActivity> implements SchedulerCallback<T> {
- private final BiPredicate<T, Boolean> mOnInitListener;
+ private BiPredicate<T, Boolean> mOnInitListener;
private final ActivityTracker<T> mActivityTracker;
private boolean mIsRegistered = false;
@@ -72,6 +72,7 @@
*/
public void unregister() {
mIsRegistered = false;
+ mOnInitListener = null;
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 5be0675..74daeca 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -16,17 +16,13 @@
package com.android.quickstep.util;
-import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static com.android.launcher3.config.FeatureFlags.FLAG_ENABLE_FIXED_ROTATION_TRANSFORM;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-import static com.android.launcher3.states.RotationHelper.FIXED_ROTATION_TRANSFORM_SETTING_NAME;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -44,19 +40,18 @@
import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
import android.view.Surface;
import androidx.annotation.IntDef;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
import java.lang.annotation.Retention;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.function.IntConsumer;
/**
* Container to hold orientation/rotation related information for Launcher.
@@ -71,6 +66,8 @@
private static final String TAG = "RecentsOrientedState";
private static final boolean DEBUG = false;
+ private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
+
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -87,46 +84,79 @@
private @SurfaceRotation int mDisplayRotation = ROTATION_0;
private @SurfaceRotation int mLauncherRotation = Surface.ROTATION_0;
- public interface SystemRotationChangeListener {
- void onSystemRotationChanged(boolean enabled);
- }
+ // Launcher activity supports multiple orientation, but fallback activity does not
+ private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
+ // Multiple orientation is only supported if density is < 600
+ private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY = 1 << 1;
+ // Feature flag controlling the multi-orientation feature
+ private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG = 1 << 2;
+ // Shared prefs for rotation, only if activity supports it
+ private static final int FLAG_HOME_ROTATION_ALLOWED_IN_PREFS = 1 << 3;
+ // If the user has enabled system rotation
+ private static final int FLAG_SYSTEM_ROTATION_ALLOWED = 1 << 4;
+ // Whether to rotation sensor is supported on the device
+ private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 5;
+ // Whether to enable rotation watcher when multi-rotation is supported
+ private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 6;
- /**
- * If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake
- * launcher orientations.
- */
- private boolean mDisableMultipleOrientations;
- private boolean mIsHomeRotationAllowed;
- private boolean mIsSystemRotationAllowed;
+ private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
+ FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
+ | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY
+ | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
+
+ private static final int MASK_ACTIVITY_ROTATING =
+ FLAG_HOME_ROTATION_ALLOWED_IN_PREFS | FLAG_SYSTEM_ROTATION_ALLOWED;
+
+ // State for which rotation watcher will be enabled.
+ // We skip it when home rotation is enabled as in that case, activity itself rotates
+ private static final int VALUE_ROTATION_WATCHER_ENABLED =
+ MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
+ | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
private final ContentResolver mContentResolver;
private final SharedPreferences mSharedPrefs;
- private final boolean mAllowConfigurationDefaultValue;
-
- private List<SystemRotationChangeListener> mSystemRotationChangeListeners = new ArrayList<>();
+ private final OrientationEventListener mOrientationListener;
private final Matrix mTmpMatrix = new Matrix();
private final Matrix mTmpInverseMatrix = new Matrix();
- public RecentsOrientedState(Context context) {
+ private int mFlags;
+ private int mPreviousRotation = ROTATION_0;
+
+ /**
+ * @param rotationChangeListener Callback for receiving rotation events when rotation watcher
+ * is enabled
+ * @see #setRotationWatcherEnabled(boolean)
+ */
+ public RecentsOrientedState(Context context, boolean rotationSupportedByActivity,
+ IntConsumer rotationChangeListener) {
mContentResolver = context.getContentResolver();
mSharedPrefs = Utilities.getPrefs(context);
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int degrees) {
+ int newRotation = getRotationForUserDegreesRotated(degrees);
+ if (newRotation != mPreviousRotation) {
+ mPreviousRotation = newRotation;
+ rotationChangeListener.accept(newRotation);
+ }
+ }
+ };
+
+ mFlags = rotationSupportedByActivity ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
Resources res = context.getResources();
int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
* res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
- mAllowConfigurationDefaultValue = originalSmallestWidth >= 600;
-
- boolean isForcedRotation = Utilities.getFeatureFlagsPrefs(context)
- .getBoolean(FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, true)
- && !mAllowConfigurationDefaultValue;
- UI_HELPER_EXECUTOR.execute(() -> {
- if (context.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) {
- Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME,
- isForcedRotation ? 1 : 0);
- }
- });
- disableMultipleOrientations(!isForcedRotation);
+ if (originalSmallestWidth < 600) {
+ mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
+ }
+ if (isFixedRotationTransformEnabled(context)) {
+ mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
+ }
+ if (mOrientationListener.canDetectOrientation()) {
+ mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
+ }
}
/**
@@ -140,10 +170,7 @@
public boolean update(
@SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation,
@SurfaceRotation int launcherRotation) {
- if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
- return false;
- }
- if (mDisableMultipleOrientations) {
+ if (!isMultipleOrientationSupportedByDevice()) {
return false;
}
if (mDisplayRotation == displayRotation && mTouchRotation == touchRotation
@@ -155,8 +182,7 @@
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
- if ((mIsHomeRotationAllowed && mIsSystemRotationAllowed) ||
- mLauncherRotation == mTouchRotation) {
+ if (canLauncherRotate() || mLauncherRotation == mTouchRotation) {
// TODO(b/153476489) Need to determine when launcher is rotated
mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
if (DEBUG) {
@@ -178,14 +204,25 @@
return true;
}
- /**
- * Setting this preference renders future calls to {@link #update(int, int, int)} as a no-op.
- */
- public void disableMultipleOrientations(boolean disable) {
- mDisableMultipleOrientations = disable;
- if (disable) {
- mDisplayRotation = mTouchRotation = ROTATION_0;
- mOrientationHandler = PagedOrientationHandler.PORTRAIT;
+ private void setFlag(int mask, boolean enabled) {
+ boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
+ && mFlags == VALUE_ROTATION_WATCHER_ENABLED;
+ if (enabled) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+
+ boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation
+ && mFlags == VALUE_ROTATION_WATCHER_ENABLED;
+ if (wasRotationEnabled != isRotationEnabled) {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ if (isRotationEnabled) {
+ mOrientationListener.enable();
+ } else {
+ mOrientationListener.disable();
+ }
+ });
}
}
@@ -195,47 +232,49 @@
}
private void updateAutoRotateSetting() {
- try {
- mIsSystemRotationAllowed = Settings.System.getInt(mContentResolver,
- Settings.System.ACCELEROMETER_ROTATION) == 1;
- } catch (Settings.SettingNotFoundException e) {
- Log.e(TAG, "autorotate setting not found", e);
- }
-
- for (SystemRotationChangeListener listener : mSystemRotationChangeListeners) {
- listener.onSystemRotationChanged(mIsSystemRotationAllowed);
- }
+ setFlag(FLAG_SYSTEM_ROTATION_ALLOWED, Settings.System.getInt(mContentResolver,
+ Settings.System.ACCELEROMETER_ROTATION, 1) == 1);
}
private void updateHomeRotationSetting() {
- mIsHomeRotationAllowed = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
- mAllowConfigurationDefaultValue);
+ setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS,
+ mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false));
}
- public void addSystemRotationChangeListener(SystemRotationChangeListener listener) {
- mSystemRotationChangeListeners.add(listener);
- listener.onSystemRotationChanged(mIsSystemRotationAllowed);
- }
-
- public void removeSystemRotationChangeListener(SystemRotationChangeListener listener) {
- mSystemRotationChangeListeners.remove(listener);
- }
-
+ /**
+ * Initializes aany system values and registers corresponding change listeners. It must be
+ * paired with {@link #destroy()} call
+ */
public void init() {
- mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
- mContentResolver.registerContentObserver(
- Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
- false, mSystemAutoRotateObserver);
+ if (isMultipleOrientationSupportedByDevice()) {
+ mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+ mContentResolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
+ false, mSystemAutoRotateObserver);
+ }
+ initWithoutListeners();
+ }
+
+ /**
+ * Unregisters any previously registered listeners.
+ */
+ public void destroy() {
+ if (isMultipleOrientationSupportedByDevice()) {
+ mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+ mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
+ }
+ setRotationWatcherEnabled(false);
+ }
+
+ /**
+ * Initializes the OrientationState without attaching any listeners. This can be used when
+ * the object is short lived.
+ */
+ public void initWithoutListeners() {
updateAutoRotateSetting();
updateHomeRotationSetting();
}
- public void destroy() {
- mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
- mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
- mSystemRotationChangeListeners.clear();
- }
-
@SurfaceRotation
public int getDisplayRotation() {
return mDisplayRotation;
@@ -251,20 +290,24 @@
return mLauncherRotation;
}
- public boolean areMultipleLayoutOrientationsDisabled() {
- return mDisableMultipleOrientations;
- }
-
- public boolean isSystemRotationAllowed() {
- return mIsSystemRotationAllowed;
+ public boolean isMultipleOrientationSupportedByDevice() {
+ return (mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
+ == MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
}
public boolean isHomeRotationAllowed() {
- return mIsHomeRotationAllowed;
+ return (mFlags & FLAG_HOME_ROTATION_ALLOWED_IN_PREFS) != 0;
}
public boolean canLauncherRotate() {
- return isSystemRotationAllowed() && isHomeRotationAllowed();
+ return (mFlags & MASK_ACTIVITY_ROTATING) == MASK_ACTIVITY_ROTATING;
+ }
+
+ /**
+ * Enables or disables the rotation watcher for listening to rotation callbacks
+ */
+ public void setRotationWatcherEnabled(boolean isEnabled) {
+ setFlag(FLAG_ROTATION_WATCHER_ENABLED, isEnabled);
}
public int getTouchRotationDegrees() {
@@ -388,4 +431,13 @@
break;
}
}
+
+ /**
+ * Returns true if system can keep Launcher fixed to portrait layout even if the
+ * foreground app is rotated
+ */
+ public static boolean isFixedRotationTransformEnabled(Context context) {
+ return Settings.Global.getInt(
+ context.getContentResolver(), FIXED_ROTATION_TRANSFORM_SETTING_NAME, 1) == 1;
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 8ecd88a..115294a 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -17,10 +17,11 @@
import static androidx.test.InstrumentationRegistry.getContext;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
-import static com.android.launcher3.ui.widget.BindWidgetTest.createWidgetInfo;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
import static org.junit.Assert.assertEquals;
@@ -187,7 +188,7 @@
LauncherSettings.Settings.call(mResolver,
LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info, true);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
addItemToScreen(item);
assertTrue("Widget is not present",
diff --git a/res/layout/secondary_launcher.xml b/res/layout/secondary_launcher.xml
index 98cfc34..fdf4446 100644
--- a/res/layout/secondary_launcher.xml
+++ b/res/layout/secondary_launcher.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/drag_layer" >
+ android:id="@+id/drag_layer"
+ android:padding="@dimen/dynamic_grid_edge_margin">
<GridView
android:layout_width="match_parent"
@@ -119,8 +120,7 @@
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
android:textColorHint="@drawable/all_apps_search_hint"
- android:textSize="16sp"
- android:translationY="24dp" />
+ android:textSize="16sp" />
<include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsContainerView>
diff --git a/res/layout/system_shortcut_icon_only.xml b/res/layout/system_shortcut_icon_only.xml
index b8b5b8c..5a81f70 100644
--- a/res/layout/system_shortcut_icon_only.xml
+++ b/res/layout/system_shortcut_icon_only.xml
@@ -19,7 +19,7 @@
android:layout_width="@dimen/system_shortcut_header_icon_touch_size"
android:layout_height="@dimen/system_shortcut_header_icon_touch_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
- android:tint="?android:attr/textColorHint"
+ android:tint="?attr/iconOnlyShortcutColor"
android:tintMode="src_in"
android:padding="@dimen/system_shortcut_header_icon_padding"
android:theme="@style/PopupItem" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e946835..d4cb6d7 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -34,6 +34,7 @@
<attr name="workspaceStatusBarScrim" format="reference" />
<attr name="widgetsTheme" format="reference" />
<attr name="loadingIconColor" format="color" />
+ <attr name="iconOnlyShortcutColor" format="color"/>
<attr name="folderDotColor" format="color" />
<attr name="folderFillColor" format="color" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0b589a2..31b12ad 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -200,7 +200,7 @@
<dimen name="system_shortcut_icon_size">24dp</dimen>
<!-- popup_arrow_center_start - system_shortcut_icon_size / 2 -->
<dimen name="system_shortcut_margin_start">16dp</dimen>
- <dimen name="system_shortcut_header_height">40dp</dimen>
+ <dimen name="system_shortcut_header_height">48dp</dimen>
<dimen name="system_shortcut_header_icon_touch_size">48dp</dimen>
<!-- (touch_size - icon_size) / 2 -->
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 65e0816..5f4bd8e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -182,7 +182,7 @@
<string name="wallpaper_button_text">Wallpapers</string>
<!-- Text for wallpaper change button -->
<string name="styles_wallpaper_button_text">Styles & wallpapers</string>
- <!-- Text for settings button -->
+ <!-- Text for settings button [CHAR LIMIT=20]-->
<string name="settings_button_text">Home settings</string>
<!-- Message shown when a feature is disabled by the administrator -->
<string name="msg_disabled_by_admin">Disabled by your admin</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1523ea9..a2c0f23 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,7 +35,7 @@
<item name="allAppsInterimScrimAlpha">46</item>
<item name="allAppsNavBarScrimColor">#66FFFFFF</item>
<item name="popupColorPrimary">#FFF</item>
- <item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
+ <item name="popupColorSecondary">#F1F3F4</item>
<item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
<item name="isMainColorDark">false</item>
<item name="isWorkspaceDarkText">false</item>
@@ -51,6 +51,7 @@
<item name="folderTextColor">#FF212121</item>
<item name="folderHintColor">#FF616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
+ <item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
<item name="workProfileOverlayTextColor">#FF212121</item>
<item name="android:windowTranslucentStatus">false</item>
@@ -93,7 +94,7 @@
<item name="allAppsInterimScrimAlpha">102</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
<item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
- <item name="popupColorSecondary">#5F6368</item> <!-- Gray 700 -->
+ <item name="popupColorSecondary">#202124</item>
<item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
<item name="folderDotColor">#FF464646</item>
@@ -103,6 +104,7 @@
<item name="folderHintColor">#FFCCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
+ <item name="iconOnlyShortcutColor">#B3FFFFFF</item>
<item name="workProfileOverlayTextColor">@android:color/white</item>
</style>
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index bbc62e9..3fa9b0a 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -22,7 +22,10 @@
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SDK_VERSION := system_current
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../tests/src_common)
+
LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.test.runner \
androidx.test.rules \
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index 0579400..c162255 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1,5 +1,6 @@
sdk=29
shadows= \
+ com.android.launcher3.shadows.LShadowApplicationPackageManager \
com.android.launcher3.shadows.LShadowAppPredictionManager \
com.android.launcher3.shadows.LShadowAppWidgetManager \
com.android.launcher3.shadows.LShadowBackupManager \
diff --git a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
new file mode 100644
index 0000000..0ca5ce6
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.secondarydisplay;
+
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.LauncherUIHelper.doLayout;
+import static com.android.launcher3.util.Preconditions.assertNotNull;
+
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.shadows.ShadowOverrides;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.LauncherModelHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
+
+/**
+ * Tests for {@link SecondaryDisplayLauncher} with work profile
+ */
+@RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
+public class SDWorkModeTest {
+
+ private static final int SYSTEM_USER = 0;
+ private static final int FLAG_SYSTEM = 0x00000800;
+ private static final int WORK_PROFILE_ID = 10;
+ private static final int FLAG_PROFILE = 0x00001000;
+
+ 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);
+ mLayoutBuilder = new LauncherLayoutBuilder();
+ }
+
+ @Test
+ public void testAllAppsList_noWorkProfile() throws Exception {
+ SecondaryDisplayLauncher launcher = loadLauncher();
+ launcher.showAppDrawer(true);
+ doLayout(launcher);
+
+ verifyRecyclerViewCount(launcher.getAppsView().getActiveRecyclerView());
+ }
+
+ @Test
+ public void testAllAppsList_workProfile() throws Exception {
+ ShadowUserManager sum = Shadow.extract(mTargetContext.getSystemService(UserManager.class));
+ sum.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
+ sum.addUser(WORK_PROFILE_ID, "work", FLAG_PROFILE);
+
+ SecondaryDisplayLauncher launcher = loadLauncher();
+ launcher.showAppDrawer(true);
+ doLayout(launcher);
+
+ AllAppsRecyclerView rv1 = launcher.getAppsView().getActiveRecyclerView();
+ verifyRecyclerViewCount(rv1);
+
+ assertNotNull(launcher.getAppsView().getWorkModeSwitch());
+ assertTrue(launcher.getAppsView().getRecyclerViewContainer() instanceof AllAppsPagedView);
+
+ AllAppsPagedView pagedView =
+ (AllAppsPagedView) launcher.getAppsView().getRecyclerViewContainer();
+ pagedView.snapToPageImmediately(1);
+ doLayout(launcher);
+
+ AllAppsRecyclerView rv2 = launcher.getAppsView().getActiveRecyclerView();
+ verifyRecyclerViewCount(rv2);
+ assertNotSame(rv1, rv2);
+ }
+
+ private SecondaryDisplayLauncher loadLauncher() throws Exception {
+ // Install 100 apps
+ for (int i = 0; i < 100; i++) {
+ mModelHelper.installApp(TEST_PACKAGE + i);
+ }
+ mModelHelper.setupDefaultLayoutProvider(new LauncherLayoutBuilder()).loadModelSync();
+ SecondaryDisplayLauncher launcher =
+ Robolectric.buildActivity(SecondaryDisplayLauncher.class).setup().get();
+ doLayout(launcher);
+ return launcher;
+ }
+
+ private void verifyRecyclerViewCount(AllAppsRecyclerView rv) {
+ int childCount = rv.getChildCount();
+ assertTrue(childCount > 0);
+ assertTrue(childCount < 100);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
new file mode 100644
index 0000000..da8b919
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
@@ -0,0 +1,36 @@
+/*
+ * 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.os.Process;
+import android.os.UserHandle;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
+
+/**
+ * Shadow for {@link ShadowApplicationPackageManager} which create mock predictors
+ */
+@Implements(className = "android.app.ApplicationPackageManager")
+public class LShadowApplicationPackageManager extends ShadowApplicationPackageManager {
+
+ @Implementation
+ public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
+ return Process.myUserHandle().equals(user) ? label : "Work " + label;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
index 76cb747..6a6f0fb 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
@@ -30,7 +30,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -80,14 +79,15 @@
protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
.resolveActivity(intent, 0);
- return ri == null ? null : getLauncherActivityInfo(ri.activityInfo);
+ return ri == null ? null : getLauncherActivityInfo(ri.activityInfo, user);
}
- public LauncherActivityInfo getLauncherActivityInfo(ActivityInfo activityInfo) {
+ public LauncherActivityInfo getLauncherActivityInfo(
+ ActivityInfo activityInfo, UserHandle user) {
return callConstructor(LauncherActivityInfo.class,
ClassParameter.from(Context.class, RuntimeEnvironment.application),
ClassParameter.from(ActivityInfo.class, activityInfo),
- ClassParameter.from(UserHandle.class, Process.myUserHandle()));
+ ClassParameter.from(UserHandle.class, user));
}
@Implementation
@@ -104,7 +104,7 @@
.setPackage(packageName);
return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
.stream()
- .map(ri -> getLauncherActivityInfo(ri.activityInfo))
+ .map(ri -> getLauncherActivityInfo(ri.activityInfo, user))
.collect(Collectors.toList());
}
@@ -130,7 +130,7 @@
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName);
return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
.stream()
- .map(ri -> getLauncherActivityInfo(ri.activityInfo))
+ .map(ri -> getLauncherActivityInfo(ri.activityInfo, user))
.collect(Collectors.toList());
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index fc3c9fd..a5f98c0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -22,7 +22,6 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.util.DisplayMetrics;
import android.view.Surface;
import com.android.launcher3.CellLayout.ContainerType;
@@ -34,6 +33,7 @@
public class DeviceProfile {
public final InvariantDeviceProfile inv;
+ private final DefaultDisplay.Info mInfo;
// Device properties
public final boolean isTablet;
@@ -133,9 +133,9 @@
public DotRenderer mDotRendererWorkSpace;
public DotRenderer mDotRendererAllApps;
- public DeviceProfile(Context context, InvariantDeviceProfile inv,
- Point minSize, Point maxSize,
- int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
+ public DeviceProfile(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info,
+ Point minSize, Point maxSize, int width, int height, boolean isLandscape,
+ boolean isMultiWindowMode, boolean transposeLayoutWithOrientation) {
this.inv = inv;
this.isLandscape = isLandscape;
@@ -152,8 +152,8 @@
availableHeightPx = maxSize.y;
}
+ mInfo = info;
Resources res = context.getResources();
- DisplayMetrics dm = res.getDisplayMetrics();
// Constants from resources
isTablet = res.getBoolean(R.bool.is_tablet);
@@ -163,8 +163,7 @@
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
// Some more constants
- transposeLayoutWithOrientation =
- res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+ this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
context = getContext(context, isVerticalBarLayout()
? Configuration.ORIENTATION_LANDSCAPE
@@ -207,13 +206,14 @@
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() ? workspacePageIndicatorHeight : 0;
- hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout()
+ hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics)
+ + (isVerticalBarLayout()
? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx)
: (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+ hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx));
// Calculate all of the remaining variables.
- updateAvailableDimensions(dm, res);
+ updateAvailableDimensions(res);
// Now that we have all of the variables calculated, we can tune certain sizes.
if (!isVerticalBarLayout() && isPhone && isTallDevice) {
@@ -227,7 +227,7 @@
hotseatBarBottomPaddingPx += extraSpace;
// Recalculate the available dimensions using the new hotseat size.
- updateAvailableDimensions(dm, res);
+ updateAvailableDimensions(res);
}
updateWorkspacePadding();
@@ -239,12 +239,21 @@
IconShape.DEFAULT_PATH_SIZE);
}
- public DeviceProfile copy(Context context) {
+ public Builder toBuilder(Context context) {
Point size = new Point(availableWidthPx, availableHeightPx);
- return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape,
- isMultiWindowMode);
+ return new Builder(context, inv, mInfo)
+ .setSizeRange(size, size)
+ .setSize(widthPx, heightPx)
+ .setMultiWindowMode(isMultiWindowMode);
}
+ public DeviceProfile copy(Context context) {
+ return toBuilder(context).build();
+ }
+
+ /**
+ * TODO: Move this to the builder as part of setMultiWindowMode
+ */
public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
// We take the minimum sizes of this profile and it's multi-window variant to ensure that
// the system decor is always excluded.
@@ -253,8 +262,11 @@
// In multi-window mode, we can have widthPx = availableWidthPx
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
- DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
- isLandscape, true);
+ DeviceProfile profile = toBuilder(context)
+ .setSizeRange(mwSize, mwSize)
+ .setSize(mwSize.x, mwSize.y)
+ .setMultiWindowMode(true)
+ .build();
// If there isn't enough vertical cell padding with the labels displayed, hide the labels.
float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
@@ -289,27 +301,30 @@
iconTextSizePx = 0;
iconDrawablePaddingPx = 0;
cellHeightPx = iconSizePx;
+ autoResizeAllAppsCells();
+ }
- // In normal cases, All Apps cell height should equal the Workspace cell height.
- // Since we are removing labels from the Workspace, we need to manually compute the
- // All Apps cell height.
+ /**
+ * Re-computes the all-apps cell size to be independent of workspace
+ */
+ public void autoResizeAllAppsCells() {
int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1);
allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx
+ Utilities.calculateTextHeight(allAppsIconTextSizePx)
+ topBottomPadding * 2;
}
- private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
- updateIconSize(1f, res, dm);
+ private void updateAvailableDimensions(Resources res) {
+ updateIconSize(1f, res);
// Check to see if the icons fit within the available height. If not, then scale down.
float usedHeight = (cellHeightPx * inv.numRows);
int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
if (usedHeight > maxHeight) {
float scale = maxHeight / usedHeight;
- updateIconSize(scale, res, dm);
+ updateIconSize(scale, res);
}
- updateAvailableFolderCellDimensions(dm, res);
+ updateAvailableFolderCellDimensions(res);
}
/**
@@ -317,12 +332,13 @@
* iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants,
* hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
*/
- private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
+ private void updateIconSize(float scale, Resources res) {
// Workspace
final boolean isVerticalLayout = isVerticalBarLayout();
float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
- iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizeDp, dm) * scale));
- iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
+ iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizeDp, mInfo.metrics)
+ * scale));
+ iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, mInfo.metrics) * scale);
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
cellHeightPx = iconSizePx + iconDrawablePaddingPx
@@ -340,8 +356,8 @@
// All apps
if (allAppsHasDifferentNumColumns()) {
- allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, dm);
- allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, dm);
+ allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, mInfo.metrics);
+ allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mInfo.metrics);
allAppsCellHeightPx = getCellSize(inv.numAllAppsColumns, inv.numAllAppsColumns).y;
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
} else {
@@ -381,12 +397,12 @@
folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
}
- private void updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res) {
+ private void updateAvailableFolderCellDimensions(Resources res) {
int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_padding_top)
+ res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
+ Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
- updateFolderCellSize(1f, dm, res);
+ updateFolderCellSize(1f, res);
// Don't let the folder get too close to the edges of the screen.
int folderMargin = edgeMarginPx * 2;
@@ -405,12 +421,12 @@
float scale = Math.min(scaleX, scaleY);
if (scale < 1f) {
- updateFolderCellSize(scale, dm, res);
+ updateFolderCellSize(scale, res);
}
}
- private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) {
- folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, dm) * scale);
+ private void updateFolderCellSize(float scale, Resources res) {
+ folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics) * scale);
folderChildTextSizePx =
(int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale);
@@ -627,4 +643,55 @@
*/
void onDeviceProfileChanged(DeviceProfile dp);
}
+
+ public static class Builder {
+ private Context mContext;
+ private InvariantDeviceProfile mInv;
+ private DefaultDisplay.Info mInfo;
+
+ private Point mMinSize, mMaxSize;
+ private int mWidth, mHeight;
+
+ private boolean mIsLandscape;
+ private boolean mIsMultiWindowMode = false;
+ private boolean mTransposeLayoutWithOrientation;
+
+ public Builder(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info) {
+ mContext = context;
+ mInv = inv;
+ mInfo = info;
+ mTransposeLayoutWithOrientation = context.getResources()
+ .getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+ }
+
+ public Builder setSizeRange(Point minSize, Point maxSize) {
+ mMinSize = minSize;
+ mMaxSize = maxSize;
+ return this;
+ }
+
+ public Builder setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ mIsLandscape = mWidth > mHeight;
+ return this;
+ }
+
+ public Builder setMultiWindowMode(boolean isMultiWindowMode) {
+ mIsMultiWindowMode = isMultiWindowMode;
+ return this;
+ }
+
+ public Builder setTransposeLayoutWithOrientation(boolean transposeLayoutWithOrientation) {
+ mTransposeLayoutWithOrientation = transposeLayoutWithOrientation;
+ return this;
+ }
+
+ public DeviceProfile build() {
+ return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize,
+ mWidth, mHeight, mIsLandscape, mIsMultiWindowMode,
+ mTransposeLayoutWithOrientation);
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d2d0863..0a1fad1 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -28,6 +28,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -159,10 +160,12 @@
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
- String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
- ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
- : null;
- initGrid(context, gridName);
+ String gridName = getCurrentGridName(context);
+ String newGridName = initGrid(context, gridName);
+ if (!newGridName.equals(gridName)) {
+ Utilities.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName).apply();
+ }
+
mConfigMonitor = new ConfigMonitor(context,
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
mOverlayMonitor = new OverlayMonitor(context);
@@ -178,17 +181,36 @@
}
}
-/**
+ /**
* This constructor should NOT have any monitors by design.
*/
public InvariantDeviceProfile(Context context, Display display) {
- initGrid(context, null, new Info(display));
+ // Ensure that the main device profile is initialized
+ InvariantDeviceProfile originalProfile = INSTANCE.get(context);
+ String gridName = getCurrentGridName(context);
+
+ // Get the display info based on default display and interpolate it to existing display
+ DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
+ DefaultDisplay.INSTANCE.get(context).getInfo(),
+ getPredefinedDeviceProfiles(context, gridName));
+
+ Info myInfo = new Info(display);
+ DisplayOption myDisplayOption = invDistWeightedInterpolate(
+ myInfo, getPredefinedDeviceProfiles(context, gridName));
+
+ DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
+ .add(myDisplayOption);
+ result.iconSize = defaultDisplayOption.iconSize;
+ result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
+ result.allAppsIconSize = Math.min(
+ defaultDisplayOption.allAppsIconSize, myDisplayOption.allAppsIconSize);
+ initGrid(context, myInfo, result);
}
public static String getCurrentGridName(Context context) {
- return Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
- ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
- : null;
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ return prefs.getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
+ ? prefs.getString(KEY_IDP_GRID_NAME, null) : null;
}
/**
@@ -203,27 +225,17 @@
}
private String initGrid(Context context, String gridName) {
- return initGrid(context, gridName, DefaultDisplay.INSTANCE.get(context).getInfo());
+ DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo();
+ ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
+
+ DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
+ initGrid(context, displayInfo, displayOption);
+ return displayOption.grid.name;
}
- private String initGrid(Context context, String gridName, DefaultDisplay.Info displayInfo) {
- Point smallestSize = new Point(displayInfo.smallestSize);
- Point largestSize = new Point(displayInfo.largestSize);
-
- ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
- // This guarantees that width < height
- float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
- displayInfo.metrics);
- float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
- displayInfo.metrics);
- // Sort the profiles based on the closeness to the device size
- Collections.sort(allOptions, (a, b) ->
- Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps),
- dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps)));
- DisplayOption interpolatedDisplayOption =
- invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions);
-
- GridOption closestProfile = allOptions.get(0).grid;
+ private void initGrid(
+ Context context, DefaultDisplay.Info displayInfo, DisplayOption displayOption) {
+ GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
@@ -236,21 +248,16 @@
mExtraAttrs = closestProfile.extraAttrs;
- if (!closestProfile.name.equals(gridName)) {
- Utilities.getPrefs(context).edit()
- .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
- }
-
- iconSize = interpolatedDisplayOption.iconSize;
+ iconSize = displayOption.iconSize;
iconShapePath = getIconShapePath(context);
- landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
+ landscapeIconSize = displayOption.landscapeIconSize;
iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics);
- iconTextSize = interpolatedDisplayOption.iconTextSize;
+ iconTextSize = displayOption.iconTextSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
if (Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
- allAppsIconSize = interpolatedDisplayOption.allAppsIconSize;
- allAppsIconTextSize = interpolatedDisplayOption.allAppsIconTextSize;
+ allAppsIconSize = displayOption.allAppsIconSize;
+ allAppsIconTextSize = displayOption.allAppsIconTextSize;
} else {
allAppsIconSize = iconSize;
allAppsIconTextSize = iconTextSize;
@@ -266,10 +273,12 @@
int smallSide = Math.min(realSize.x, realSize.y);
int largeSide = Math.max(realSize.x, realSize.y);
- landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
- largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
- portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
- smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
+ DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo)
+ .setSizeRange(new Point(displayInfo.smallestSize),
+ new Point(displayInfo.largestSize));
+
+ landscapeProfile = builder.setSize(largeSide, smallSide).build();
+ portraitProfile = builder.setSize(smallSide, largeSide).build();
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
@@ -283,8 +292,6 @@
ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
-
- return closestProfile.name;
}
@Nullable
@@ -454,6 +461,41 @@
}
@VisibleForTesting
+ static DisplayOption invDistWeightedInterpolate(
+ DefaultDisplay.Info displayInfo, ArrayList<DisplayOption> points) {
+ Point smallestSize = new Point(displayInfo.smallestSize);
+ Point largestSize = new Point(displayInfo.largestSize);
+
+ // This guarantees that width < height
+ float width = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
+ displayInfo.metrics);
+ float height = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
+ displayInfo.metrics);
+
+ // Sort the profiles based on the closeness to the device size
+ Collections.sort(points, (a, b) ->
+ Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
+ dist(width, height, b.minWidthDps, b.minHeightDps)));
+
+ GridOption closestOption = points.get(0).grid;
+ float weights = 0;
+
+ DisplayOption p = points.get(0);
+ if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
+ return p;
+ }
+
+ DisplayOption out = new DisplayOption(closestOption);
+ for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
+ p = points.get(i);
+ float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
+ weights += w;
+ out.add(new DisplayOption().add(p).multiply(w));
+ }
+ return out.multiply(1.0f / weights);
+ }
+
+ @VisibleForTesting
static DisplayOption invDistWeightedInterpolate(float width, float height,
ArrayList<DisplayOption> points) {
float weights = 0;
@@ -573,7 +615,6 @@
private static final class DisplayOption {
private final GridOption grid;
- private final String name;
private final float minWidthDps;
private final float minHeightDps;
private final boolean canBeDefault;
@@ -590,7 +631,6 @@
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ProfileDisplayOption);
- name = a.getString(R.styleable.ProfileDisplayOption_name);
minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
canBeDefault = a.getBoolean(
@@ -609,8 +649,11 @@
}
DisplayOption() {
- grid = null;
- name = null;
+ this(null);
+ }
+
+ DisplayOption(GridOption grid) {
+ this.grid = grid;
minWidthDps = 0;
minHeightDps = 0;
canBeDefault = false;
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 33262b6..6692af5 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.AsyncTask;
@@ -30,7 +31,6 @@
import android.widget.Switch;
import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
@@ -79,9 +79,8 @@
@Override
public void toggle() {
- Launcher launcher = Launcher.getLauncher(getContext());
// don't show tip if user uses toggle
- launcher.getSharedPrefs().edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
+ Utilities.getPrefs(getContext()).edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
trySetQuietModeEnabledToAllProfilesAsync(isChecked());
}
@@ -203,9 +202,8 @@
}
private boolean shouldShowWorkSwitch() {
- Launcher launcher = Launcher.getLauncher(getContext());
- return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
- || launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ return Utilities.ATLEAST_P && (hasShortcutsPermission(getContext())
+ || getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
== PackageManager.PERMISSION_GRANTED);
}
@@ -213,12 +211,14 @@
* Shows a work tip on the Nth work tab open
*/
public void showTipifNeeded() {
- Launcher launcher = Launcher.getLauncher(getContext());
- int tipCounter = launcher.getSharedPrefs().getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
+ Context context = getContext();
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ int tipCounter = prefs.getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
if (tipCounter < 0) return;
if (tipCounter == 0) {
- new ArrowTipView(launcher).show(launcher.getString(R.string.work_switch_tip), getTop());
+ new ArrowTipView(context)
+ .show(context.getString(R.string.work_switch_tip), getTop());
}
- launcher.getSharedPrefs().edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
+ prefs.edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
}
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2b91cb1..65d3cd2 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -159,10 +159,6 @@
"ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
"Always use hardware optimization for folder animations.");
- public static final BooleanFlag ENABLE_FIXED_ROTATION_TRANSFORM = getDebugFlag(
- FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, true,
- "Launch/close apps without rotation animation. Fix Launcher to portrait");
-
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index f1f271f..dd6fc49 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -89,17 +89,16 @@
if (mDragLayer != null) {
return;
}
- InvariantDeviceProfile mainIdp = LauncherAppState.getIDP(this);
InvariantDeviceProfile currentDisplayIdp =
new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay());
- // Pick the device profile with the smaller icon size so that the cached icons are
- // shown properly
- if (mainIdp.iconBitmapSize <= currentDisplayIdp.iconBitmapSize) {
- mDeviceProfile = mainIdp.getDeviceProfile(this).copy(this);
- } else {
- mDeviceProfile = currentDisplayIdp.getDeviceProfile(this);
- }
+ // Disable transpose layout and use multi-window mode so that the icons are scaled properly
+ mDeviceProfile = currentDisplayIdp.getDeviceProfile(this)
+ .toBuilder(this)
+ .setMultiWindowMode(true)
+ .setTransposeLayoutWithOrientation(false)
+ .build();
+ mDeviceProfile.autoResizeAllAppsCells();
setContentView(R.layout.secondary_launcher);
mDragLayer = findViewById(R.id.drag_layer);
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index e35e884..40630d3 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -117,10 +117,12 @@
if (child == mAppsView) {
int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
+ grid.cellLayoutPaddingLeftRightPx);
- int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
+ int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
int appsWidth = Math.min(width, maxWidth);
- int appsHeight = Math.round(appsWidth * (float) height / (float) width);
+
+ int maxHeight = grid.allAppsCellHeightPx * idp.numAllAppsColumns + padding;
+ int appsHeight = Math.min(height, maxHeight);
mAppsView.measure(
makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY));
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 3640d8b..5a60f53 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -43,7 +43,6 @@
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
- public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
private final ContentResolver mContentResolver;
private boolean mSystemAutoRotateEnabled;
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 60470dc..a7575d1 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -32,7 +32,7 @@
import androidx.core.content.ContextCompat;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
@@ -48,13 +48,13 @@
private static final long SHOW_DURATION_MS = 300;
private static final long HIDE_DURATION_MS = 100;
- protected final Launcher mLauncher;
+ protected final BaseDraggingActivity mActivity;
private final Handler mHandler = new Handler();
private Runnable mOnClosed;
public ArrowTipView(Context context) {
super(context, null, 0);
- mLauncher = Launcher.getLauncher(context);
+ mActivity = BaseDraggingActivity.fromContext(context);
init(context);
}
@@ -75,11 +75,11 @@
.setStartDelay(0)
.setDuration(HIDE_DURATION_MS)
.setInterpolator(Interpolators.ACCEL)
- .withEndAction(() -> mLauncher.getDragLayer().removeView(this))
+ .withEndAction(() -> mActivity.getDragLayer().removeView(this))
.start();
} else {
animate().cancel();
- mLauncher.getDragLayer().removeView(this);
+ mActivity.getDragLayer().removeView(this);
}
if (mOnClosed != null) mOnClosed.run();
mIsOpen = false;
@@ -126,12 +126,12 @@
*/
public ArrowTipView show(String text, int top) {
((TextView) findViewById(R.id.text)).setText(text);
- mLauncher.getDragLayer().addView(this);
+ mActivity.getDragLayer().addView(this);
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
params.gravity = Gravity.CENTER_HORIZONTAL;
- params.leftMargin = mLauncher.getDeviceProfile().workspacePadding.left;
- params.rightMargin = mLauncher.getDeviceProfile().workspacePadding.right;
+ params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
+ params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
post(() -> setY(top - getHeight()));
setAlpha(0);
animate()
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 5b3840f..6e21512 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
-import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.animation.Animator;
@@ -100,6 +99,7 @@
private AnimatorSet mFadeAnimatorSet;
private ListenerView mListenerView;
+ private Runnable mFastFinishRunnable;
public FloatingIconView(Context context) {
this(context, null);
@@ -443,9 +443,21 @@
}
}
+ /**
+ * Sets a runnable that is called after a call to {@link #fastFinish()}.
+ */
+ public void setFastFinishRunnable(Runnable runnable) {
+ mFastFinishRunnable = runnable;
+ }
+
public void fastFinish() {
+ if (mFastFinishRunnable != null) {
+ mFastFinishRunnable.run();
+ mFastFinishRunnable = null;
+ }
if (mLoadIconSignal != null) {
mLoadIconSignal.cancel();
+ mLoadIconSignal = null;
}
if (mEndRunnable != null) {
mEndRunnable.run();
@@ -655,6 +667,7 @@
sTmpObjArray[0] = null;
mIconLoadResult = null;
mClipIconView.recycle();
+ mFastFinishRunnable = null;
}
private static class IconLoadResult {
diff --git a/tests/Android.mk b/tests/Android.mk
index a9fff8e..4d1bfa6 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -62,7 +62,11 @@
LOCAL_STATIC_JAVA_LIBRARIES += ub-launcher-aosp-tapl
endif
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, src_common)
+
+
LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_PACKAGE_NAME := Launcher3Tests
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 86faddb..0ba5a36 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,7 +17,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -26,7 +25,6 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -53,6 +51,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities;
+import com.android.launcher3.common.WidgetUtils;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -60,7 +59,6 @@
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Wait;
@@ -306,26 +304,7 @@
* Adds {@param item} on the homescreen on the 0th screen
*/
protected void addItemToScreen(ItemInfo item) {
- ContentResolver resolver = mTargetContext.getContentResolver();
- int screenId = FIRST_SCREEN_ID;
- // Update the screen id counter for the provider.
- LauncherSettings.Settings.call(resolver,
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
- if (screenId > FIRST_SCREEN_ID) {
- screenId = FIRST_SCREEN_ID;
- }
-
- // Insert the item
- ContentWriter writer = new ContentWriter(mTargetContext);
- item.id = LauncherSettings.Settings.call(
- resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- item.screenId = screenId;
- item.onAddToDatabase(writer);
- writer.put(LauncherSettings.Favorites._ID, item.id);
- resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
- writer.getValues(mTargetContext));
+ WidgetUtils.addItemToScreen(item, mTargetContext);
resetLoaderState();
// Launch the home activity
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 62ce085..df11557 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -17,18 +17,16 @@
import static androidx.test.InstrumentationRegistry.getTargetContext;
-import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
+import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
-import android.content.Context;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
@@ -39,7 +37,6 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -50,7 +47,6 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import org.junit.After;
@@ -108,7 +104,7 @@
@Test
public void testBindNormalWidget_withConfig() {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
- LauncherAppWidgetInfo item = createWidgetInfo(info, true);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
addItemToScreen(item);
verifyWidgetPresent(info);
@@ -117,7 +113,7 @@
@Test
public void testBindNormalWidget_withoutConfig() {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info, true);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
addItemToScreen(item);
verifyWidgetPresent(info);
@@ -126,7 +122,7 @@
@Test
public void testUnboundWidget_removed() {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
- LauncherAppWidgetInfo item = createWidgetInfo(info, false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
item.appWidgetId = -33;
addItemToScreen(item);
@@ -147,7 +143,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
// Do not bind the widget
- LauncherAppWidgetInfo item = createWidgetInfo(info, false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
addItemToScreen(item);
@@ -160,7 +156,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
// Do not bind the widget
- LauncherAppWidgetInfo item = createWidgetInfo(info, false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
addItemToScreen(item);
@@ -282,47 +278,6 @@
}
/**
- * Creates a LauncherAppWidgetInfo corresponding to {@param info}
- *
- * @param bindWidget if true the info is bound and a valid widgetId is assigned to
- * the LauncherAppWidgetInfo
- */
- public static LauncherAppWidgetInfo createWidgetInfo(
- LauncherAppWidgetProviderInfo info, boolean bindWidget) {
- Context targetContext = getTargetContext();
-
- LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
- LauncherAppWidgetInfo.NO_ID, info.provider);
- item.spanX = info.minSpanX;
- item.spanY = info.minSpanY;
- item.minSpanX = info.minSpanX;
- item.minSpanY = info.minSpanY;
- item.user = info.getProfile();
- item.cellX = 0;
- item.cellY = 1;
- item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
-
- if (bindWidget) {
- PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(info);
- pendingInfo.spanX = item.spanX;
- pendingInfo.spanY = item.spanY;
- pendingInfo.minSpanX = item.minSpanX;
- pendingInfo.minSpanY = item.minSpanY;
- Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
-
- AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
- int widgetId = host.allocateAppWidgetId();
- if (!new WidgetManagerHelper(targetContext)
- .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
- host.deleteAppWidgetId(widgetId);
- throw new IllegalArgumentException("Unable to bind widget id");
- }
- item.appWidgetId = widgetId;
- }
- return item;
- }
-
- /**
* Returns a LauncherAppWidgetInfo with package name which is not present on the device
*/
private LauncherAppWidgetInfo getInvalidWidgetInfo() {
diff --git a/tests/src_common/README.md b/tests/src_common/README.md
new file mode 100644
index 0000000..2bc9e73
--- /dev/null
+++ b/tests/src_common/README.md
@@ -0,0 +1 @@
+Common source code used by both android tests and robolectric tests
\ No newline at end of file
diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
new file mode 100644
index 0000000..c0913bf
--- /dev/null
+++ b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
@@ -0,0 +1,104 @@
+/*
+ * 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.common;
+
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
+
+import android.appwidget.AppWidgetHost;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.launcher3.LauncherAppWidgetHost;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetManagerHelper;
+
+/**
+ * Common method for widget binding
+ */
+public class WidgetUtils {
+
+ /**
+ * Creates a LauncherAppWidgetInfo corresponding to {@param info}
+ *
+ * @param bindWidget if true the info is bound and a valid widgetId is assigned to
+ * the LauncherAppWidgetInfo
+ */
+ public static LauncherAppWidgetInfo createWidgetInfo(
+ LauncherAppWidgetProviderInfo info, Context targetContext, boolean bindWidget) {
+ LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
+ LauncherAppWidgetInfo.NO_ID, info.provider);
+ item.spanX = info.minSpanX;
+ item.spanY = info.minSpanY;
+ item.minSpanX = info.minSpanX;
+ item.minSpanY = info.minSpanY;
+ item.user = info.getProfile();
+ item.cellX = 0;
+ item.cellY = 1;
+ item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+
+ if (bindWidget) {
+ PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(info);
+ pendingInfo.spanX = item.spanX;
+ pendingInfo.spanY = item.spanY;
+ pendingInfo.minSpanX = item.minSpanX;
+ pendingInfo.minSpanY = item.minSpanY;
+ Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
+
+ AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
+ int widgetId = host.allocateAppWidgetId();
+ if (!new WidgetManagerHelper(targetContext)
+ .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
+ host.deleteAppWidgetId(widgetId);
+ throw new IllegalArgumentException("Unable to bind widget id");
+ }
+ item.appWidgetId = widgetId;
+ }
+ return item;
+ }
+
+ /**
+ * Adds {@param item} on the homescreen on the 0th screen
+ */
+ public static void addItemToScreen(ItemInfo item, Context targetContext) {
+ ContentResolver resolver = targetContext.getContentResolver();
+ int screenId = FIRST_SCREEN_ID;
+ // Update the screen id counter for the provider.
+ LauncherSettings.Settings.call(resolver,
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+ if (screenId > FIRST_SCREEN_ID) {
+ screenId = FIRST_SCREEN_ID;
+ }
+
+ // Insert the item
+ ContentWriter writer = new ContentWriter(targetContext);
+ item.id = LauncherSettings.Settings.call(
+ resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+ .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ item.screenId = screenId;
+ item.onAddToDatabase(writer);
+ writer.put(LauncherSettings.Favorites._ID, item.id);
+ resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
+ writer.getValues(targetContext));
+ }
+}