Merge "Have separate DeviceProfile instances for taskbar and all apps windows." into tm-qpr-dev
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 21728ad..d676f7d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1996,9 +1996,11 @@
* Applies the transform on the recents animation
*/
protected void applyScrollAndTransform() {
- // No need to apply any transform if there is ongoing swipe-pip-to-home animator since
- // that animator handles the leash solely.
- boolean notSwipingPipToHome = mRecentsAnimationTargets != null && !mIsSwipingPipToHome;
+ // No need to apply any transform if there is ongoing swipe-to-home animator
+ // swipe-to-pip handles the leash solely
+ // swipe-to-icon animation is handled by RectFSpringAnim anim
+ boolean notSwipingToHome = mRecentsAnimationTargets != null
+ && mGestureState.getEndTarget() != HOME;
boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
AnimatorControllerWithResistance playbackController =
@@ -2008,7 +2010,7 @@
getScaleProgressDueToScroll()), mDragLengthFactor);
}
- if (notSwipingPipToHome) {
+ if (notSwipingToHome) {
TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator();
if (setRecentsScroll) {
taskViewSimulator.setScroll(mRecentsView.getScrollOffset());
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index ee5bb44..99f7bdd 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -23,16 +23,15 @@
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
@@ -161,11 +160,7 @@
if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo);
}
- try {
- mContext.startActivity(intent, options.toBundle());
- } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
- mContext.startActivity(createHomeIntent());
- }
+ startHomeIntentSafely(mContext, intent, options.toBundle());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0efe666..9e3173c 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,11 +20,11 @@
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -33,8 +33,12 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.tracing.OverviewComponentObserverProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -276,4 +280,34 @@
overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
}
+
+ /**
+ * Starts the intent for the current home activity.
+ */
+ public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options) {
+ RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context);
+ OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState);
+ Intent intent = observer.getHomeIntent();
+ observer.onDestroy();
+ deviceState.destroy();
+ startHomeIntentSafely(context, intent, options);
+ }
+
+ /**
+ * Starts the intent for the current home activity.
+ */
+ public static void startHomeIntentSafely(
+ @NonNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options) {
+ try {
+ context.startActivity(homeIntent, options);
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ context.startActivity(createHomeIntent(), options);
+ }
+ }
+
+ private static Intent createHomeIntent() {
+ return new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 4f0b976..67ce606 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -21,11 +21,11 @@
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -428,7 +428,7 @@
RemoteAnimationAdapterCompat adapterCompat =
new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
getIApplicationThread());
- startActivity(createHomeIntent(),
+ startHomeIntentSafely(this,
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index d7ee3cb..76513a4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -268,6 +268,16 @@
MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn);
}
+ /**
+ * Preloads the Overview activity.
+ *
+ * This method should only be used when the All Set page of the SUW is reached to safely
+ * preload the Launcher for the SUW first reveal.
+ */
+ public void preloadOverviewForSUWAllSet() {
+ preloadOverview(false, true);
+ }
+
@Override
public void onRotationProposal(int rotation, boolean isValid) {
executeForTaskbarManager(() -> mTaskbarManager.onRotationProposal(rotation, isValid));
@@ -883,6 +893,10 @@
}
private void preloadOverview(boolean fromInit) {
+ preloadOverview(fromInit, false);
+ }
+
+ private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
if (!mDeviceState.isUserUnlocked()) {
return;
}
@@ -892,7 +906,8 @@
return;
}
- if (RestoreDbTask.isPending(this) || !mDeviceState.isUserSetupComplete()) {
+ if ((RestoreDbTask.isPending(this) && !forSUWAllSet)
+ || !mDeviceState.isUserSetupComplete()) {
// Preloading while a restore is pending may cause launcher to start the restore
// too early.
return;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 3d737ca..3c0da01 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -19,12 +19,12 @@
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.animation.Animator;
@@ -205,8 +205,9 @@
@Override
public void onAnimationEnd(Animator animation) {
if (dismissTask) {
- // For now, just start the home intent so user is prompted to unlock the device.
- mContext.startActivity(createHomeIntent());
+ // For now, just start the home intent so user is prompted to
+ // unlock the device.
+ startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
mHomeLaunched = true;
}
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 864e08d..a730183 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,12 +15,11 @@
*/
package com.android.quickstep.inputconsumers;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
@@ -79,11 +78,7 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
- try {
- mContext.startActivity(mGestureState.getHomeIntent());
- } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
- mContext.startActivity(createHomeIntent());
- }
+ startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int state = (mGestureState != null && mGestureState.getEndTarget() != null)
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index a379aad..66ed056 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.Utilities.mapRange;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import android.animation.Animator;
import android.app.Activity;
@@ -148,45 +149,50 @@
}
private void startBackgroundAnimation() {
- if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_THUD)) {
- if (mBackgroundAnimatorListener == null) {
- mBackgroundAnimatorListener =
- new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- runOnUiHelperThread(mVibrator::cancel);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- runOnUiHelperThread(mVibrator::cancel);
- }
- };
- }
- mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+ if (!Utilities.ATLEAST_S || mVibrator == null) {
+ return;
}
- mAnimatedBackground.playAnimation();
- }
+ boolean supportsThud = mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_THUD);
- /**
- * Sets up the vibration effect for the next round of animation. The parameters vary between
- * different illustrations.
- */
- private VibrationEffect getVibrationEffect() {
- return VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, 50)
- .compose();
+ if (!supportsThud && !mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ return;
+ }
+ if (mBackgroundAnimatorListener == null) {
+ VibrationEffect vibrationEffect = VibrationEffect.startComposition()
+ .addPrimitive(supportsThud
+ ? VibrationEffect.Composition.PRIMITIVE_THUD
+ : VibrationEffect.Composition.PRIMITIVE_TICK,
+ /* scale= */ 1.0f,
+ /* delay= */ 50)
+ .compose();
+
+ mBackgroundAnimatorListener =
+ new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect));
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect));
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ runOnUiHelperThread(mVibrator::cancel);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ runOnUiHelperThread(mVibrator::cancel);
+ }
+ };
+ }
+ mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+ mAnimatedBackground.playAnimation();
}
@Override
@@ -202,6 +208,7 @@
mBinder = binder;
mBinder.getTaskbarManager().setSetupUIVisible(isResumed());
mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
+ mBinder.preloadOverviewForSUWAllSet();
}
@Override
@@ -281,7 +288,7 @@
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == AccessibilityAction.ACTION_CLICK.getId()) {
- startActivity(Utilities.createHomeIntent());
+ startHomeIntentSafely(AllSetActivity.this, null);
finish();
return true;
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index c980d1e..45fc3af 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,5 +1,6 @@
package com.android.quickstep.views;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -19,6 +20,7 @@
import androidx.annotation.Nullable;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.LauncherAnimUtils;
@@ -110,13 +112,19 @@
*/
public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) {
- final BaseDragLayer dragLayer = launcher.getDragLayer();
- ViewGroup parent = (ViewGroup) dragLayer.getParent();
+ final ViewGroup dragLayer = launcher.getDragLayer();
final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater()
- .inflate(R.layout.floating_split_select_view, parent, false);
+ .inflate(R.layout.floating_split_select_view, dragLayer, false);
floatingView.init(launcher, originalView, thumbnail, icon, positionOut);
- parent.addView(floatingView);
+ // Add this animating view underneath the existing open task menu view (if there is one)
+ View openTaskView = AbstractFloatingView.getOpenView(launcher, TYPE_TASK_MENU);
+ int openTaskViewIndex = dragLayer.indexOfChild(openTaskView);
+ if (openTaskViewIndex == -1) {
+ // Add to top if not
+ openTaskViewIndex = dragLayer.getChildCount();
+ }
+ dragLayer.addView(floatingView, openTaskViewIndex - 1);
return floatingView;
}
@@ -125,7 +133,7 @@
Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), originalView, viewBounds,
false /* ignoreTransform */, null /* recycle */,
mStartingPosition);
- final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
+ final BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
Math.round(mStartingPosition.width()),
Math.round(mStartingPosition.height()));
initPosition(mStartingPosition, lp);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a2f6792..364bd59 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -34,6 +34,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -4097,6 +4098,10 @@
mSecondSplitHiddenView.setVisibility(INVISIBLE);
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
+
+ // Fade out all other views underneath placeholders
+ ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0);
+ pendingAnimation.add(tvFade, DEACCEL_2, SpringProperty.DEFAULT);
pendingAnimation.buildAnim().start();
return true;
}
@@ -4110,11 +4115,11 @@
mSplitInstructionsView = null;
}
if (mFirstFloatingTaskView != null) {
- mActivity.getRootView().removeView(mFirstFloatingTaskView);
+ mActivity.getDragLayer().removeView(mFirstFloatingTaskView);
mFirstFloatingTaskView = null;
}
if (mSecondFloatingTaskView != null) {
- mActivity.getRootView().removeView(mSecondFloatingTaskView);
+ mActivity.getDragLayer().removeView(mSecondFloatingTaskView);
mSecondFloatingTaskView = null;
mSecondSplitHiddenView.setVisibility(VISIBLE);
mSecondSplitHiddenView = null;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d58bb7c..f0ca46a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1537,7 +1537,6 @@
}
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
}
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index c16792a..7b2ed8b 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -31,6 +31,7 @@
<dimen name="drop_target_button_drawable_horizontal_padding">24dp</dimen>
<dimen name="drop_target_button_drawable_vertical_padding">20dp</dimen>
<dimen name="drop_target_button_gap">32dp</dimen>
+ <dimen name="drop_target_button_workspace_edge_gap">32dp</dimen>
<dimen name="drop_target_top_margin">110dp</dimen>
<dimen name="drop_target_bottom_margin">48dp</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e7b3375..39f0a2b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -241,6 +241,7 @@
<dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
<dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
<dimen name="drop_target_button_gap">28dp</dimen>
+ <dimen name="drop_target_button_workspace_edge_gap">0dp</dimen>
<!-- the distance an icon must be dragged before button drop targets accept it -->
<dimen name="drag_distanceThreshold">30dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 7ad1412..af21b27 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -37,4 +37,5 @@
<item type="id" name="quick_settings_button" />
<item type="id" name="notifications_button" />
+ <item type="id" name="cache_entry_tag_id" />
</resources>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 90869c2..21dbc5f 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -93,6 +93,7 @@
public static final int TYPE_WIDGETS_EDUCATION_DIALOG = 1 << 15;
public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
public static final int TYPE_TASKBAR_ALL_APPS = 1 << 17;
+ public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 18;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
@@ -100,7 +101,7 @@
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
| TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS
- | TYPE_OPTIONS_POPUP_DIALOG;
+ | TYPE_OPTIONS_POPUP_DIALOG | TYPE_ADD_TO_HOME_CONFIRMATION;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 8da4f05..3b24df2 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -179,7 +179,12 @@
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- mActive = !options.isKeyboardDrag && supportsDrop(dragObject.dragInfo);
+ if (options.isKeyboardDrag) {
+ mActive = false;
+ } else {
+ setupItemInfo(dragObject.dragInfo);
+ mActive = supportsDrop(dragObject.dragInfo);
+ }
setVisibility(mActive ? View.VISIBLE : View.GONE);
mAccessibleDrag = options.isAccessibleDrag;
@@ -191,6 +196,11 @@
return supportsDrop(dragObject.dragInfo);
}
+ /**
+ * Setups button for the specified ItemInfo.
+ */
+ protected abstract void setupItemInfo(ItemInfo info);
+
protected abstract boolean supportsDrop(ItemInfo info);
public abstract boolean supportsAccessibilityDrop(ItemInfo info, View view);
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 371bb80..95d3ad9 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -85,6 +85,9 @@
}
@Override
+ protected void setupItemInfo(ItemInfo info) {}
+
+ @Override
protected boolean supportsDrop(ItemInfo info) {
return true;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 827c5b8..8a82aeb 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -217,6 +217,7 @@
public int dropTargetHorizontalPaddingPx;
public int dropTargetVerticalPaddingPx;
public int dropTargetGapPx;
+ public int dropTargetButtonWorkspaceEdgeGapPx;
// Insets
private final Rect mInsets = new Rect();
@@ -348,6 +349,8 @@
dropTargetVerticalPaddingPx = res.getDimensionPixelSize(
R.dimen.drop_target_button_drawable_vertical_padding);
dropTargetGapPx = res.getDimensionPixelSize(R.dimen.drop_target_button_gap);
+ dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize(
+ R.dimen.drop_target_button_workspace_edge_gap);
workspaceSpringLoadedBottomSpace =
res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index dbddb26..d908440 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -186,14 +186,13 @@
availableWidth = scaledPanelWidth - halfButtonGap / 2;
} else {
// Both buttons plus the button gap do not display past the edge of the scaled
- // workspace.
- availableWidth = (scaledPanelWidth - dp.dropTargetGapPx) / 2;
+ // workspace, less a pre-defined gap from the edge of the workspace.
+ availableWidth = scaledPanelWidth - dp.dropTargetGapPx
+ - 2 * dp.dropTargetButtonWorkspaceEdgeGapPx;
}
int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
firstButton.measure(widthSpec, heightSpec);
- secondButton.measure(widthSpec, heightSpec);
-
if (!mIsVertical) {
// Remove icons and put the button's text on two lines if text is truncated.
if (firstButton.isTextTruncated(availableWidth)) {
@@ -202,6 +201,14 @@
firstButton.setPadding(horizontalPadding, verticalPadding / 2,
horizontalPadding, verticalPadding / 2);
}
+ }
+
+ if (!dp.isTwoPanels) {
+ availableWidth -= firstButton.getMeasuredWidth() + dp.dropTargetGapPx;
+ widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
+ }
+ secondButton.measure(widthSpec, heightSpec);
+ if (!mIsVertical) {
if (secondButton.isTextTruncated(availableWidth)) {
secondButton.setIconVisible(false);
secondButton.setTextMultiLine(true);
@@ -243,13 +250,30 @@
int buttonGap = dp.dropTargetGapPx;
ButtonDropTarget leftButton = mTempTargets[0];
- leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
- barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
-
ButtonDropTarget rightButton = mTempTargets[1];
- rightButton.layout(barCenter + (buttonGap / 2), 0,
- barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(),
- rightButton.getMeasuredHeight());
+ if (dp.isTwoPanels) {
+ leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
+ barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
+ rightButton.layout(barCenter + (buttonGap / 2), 0,
+ barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(),
+ rightButton.getMeasuredHeight());
+ } else {
+ int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale);
+
+ int leftButtonWidth = leftButton.getMeasuredWidth();
+ int rightButtonWidth = rightButton.getMeasuredWidth();
+ int extraSpace = scaledPanelWidth - leftButtonWidth - rightButtonWidth - buttonGap;
+
+ int leftButtonStart = barCenter - (scaledPanelWidth / 2) + extraSpace / 2;
+ int leftButtonEnd = leftButtonStart + leftButtonWidth;
+ int rightButtonStart = leftButtonEnd + buttonGap;
+ int rightButtonEnd = rightButtonStart + rightButtonWidth;
+
+ leftButton.layout(leftButtonStart, 0, leftButtonEnd,
+ leftButton.getMeasuredHeight());
+ rightButton.layout(rightButtonStart, 0, rightButtonEnd,
+ rightButton.getMeasuredHeight());
+ }
}
}
@@ -318,7 +342,7 @@
}
public ButtonDropTarget[] getDropTargets() {
- return mDropTargets;
+ return getVisibility() == View.VISIBLE ? mDropTargets : new ButtonDropTarget[0];
}
@Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 31c1eff..1846383 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -536,6 +536,7 @@
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
+ setTitle(R.string.home_screen);
}
protected LauncherOverlayManager getDefaultOverlay() {
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 5b037e4..f8bc1f4 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -6,6 +6,7 @@
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.INVALID;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DISMISS_PREDICTION_UNDO;
@@ -69,6 +70,7 @@
private boolean mHadPendingAlarm;
protected int mCurrentAccessibilityAction = -1;
+
public SecondaryDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -134,24 +136,33 @@
}
@Override
+ protected void setupItemInfo(ItemInfo info) {
+ int buttonType = getButtonType(info, getViewUnderDrag(info));
+ if (buttonType != INVALID) {
+ setupUi(buttonType);
+ }
+ }
+
+ @Override
protected boolean supportsDrop(ItemInfo info) {
- return supportsAccessibilityDrop(info, getViewUnderDrag(info));
+ return getButtonType(info, getViewUnderDrag(info)) != INVALID;
}
@Override
public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
+ return getButtonType(info, view) != INVALID;
+ }
+
+ private int getButtonType(ItemInfo info, View view) {
if (view instanceof AppWidgetHostView) {
if (getReconfigurableWidgetId(view) != INVALID_APPWIDGET_ID) {
- setupUi(RECONFIGURE);
- return true;
+ return RECONFIGURE;
}
- return false;
+ return INVALID;
} else if (FeatureFlags.ENABLE_PREDICTION_DISMISS.get() && info.isPredictedItem()) {
- setupUi(DISMISS_PREDICTION);
- return true;
+ return DISMISS_PREDICTION;
}
- setupUi(UNINSTALL);
Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user);
if (uninstallDisabled == null) {
UserManager userManager =
@@ -165,16 +176,20 @@
mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
mCacheExpireAlarm.setOnAlarmListener(this);
if (uninstallDisabled) {
- return false;
+ return INVALID;
}
if (info instanceof ItemInfoWithIcon) {
ItemInfoWithIcon iconInfo = (ItemInfoWithIcon) info;
- if ((iconInfo.runtimeStatusFlags & FLAG_SYSTEM_MASK) != 0) {
- return (iconInfo.runtimeStatusFlags & FLAG_SYSTEM_NO) != 0;
+ if ((iconInfo.runtimeStatusFlags & FLAG_SYSTEM_MASK) != 0
+ && (iconInfo.runtimeStatusFlags & FLAG_SYSTEM_NO) == 0) {
+ return INVALID;
}
}
- return getUninstallTarget(info) != null;
+ if (getUninstallTarget(info) == null) {
+ return INVALID;
+ }
+ return UNINSTALL;
}
/**
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7b96838..dc8c739 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -587,15 +587,6 @@
}
/**
- * Returns an intent for starting the default home activity
- */
- public static Intent createHomeIntent() {
- return new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
-
- /**
* Wraps a message with a TTS span, so that a different message is spoken than
* what is getting displayed.
* @param msg original message
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 44d57d7..79214e8 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -60,6 +60,7 @@
public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction;
public static final int PIN_PREDICTION = R.id.action_pin_prediction;
public static final int RECONFIGURE = R.id.action_reconfigure;
+ public static final int INVALID = -1;
protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
protected static final int MOVE = R.id.action_move;
protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index fc52797..dd0b503 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -246,7 +246,7 @@
*/
public boolean shouldContainerScroll(MotionEvent ev) {
// Scroll if not within the container view (e.g. over large-screen scrim).
- if (!mActivityContext.getDragLayer().isEventOverView(this, ev)) {
+ if (!mActivityContext.getDragLayer().isEventOverView(getVisibleContainerView(), ev)) {
return true;
}
if (mActivityContext.getDragLayer().isEventOverView(mBottomSheetHandleArea, ev)) {
diff --git a/src/com/android/launcher3/anim/AnimatedPropertySetter.java b/src/com/android/launcher3/anim/AnimatedPropertySetter.java
new file mode 100644
index 0000000..e5f5e7c
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimatedPropertySetter.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.anim;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.graphics.drawable.ColorDrawable;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+/**
+ * Extension of {@link PropertySetter} which applies the property through an animation
+ */
+public class AnimatedPropertySetter extends PropertySetter {
+
+ protected final AnimatorSet mAnim = new AnimatorSet();
+ protected ValueAnimator mProgressAnimator;
+
+ @Override
+ public Animator setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ if (view == null || view.getAlpha() == alpha) {
+ return NO_OP;
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
+ anim.addListener(new AlphaUpdateListener(view));
+ anim.setInterpolator(interpolator);
+ add(anim);
+ return anim;
+ }
+
+ @Override
+ public Animator setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
+ if (view == null || (view.getBackground() instanceof ColorDrawable
+ && ((ColorDrawable) view.getBackground()).getColor() == color)) {
+ return NO_OP;
+ }
+ ObjectAnimator anim = ObjectAnimator.ofArgb(view, VIEW_BACKGROUND_COLOR, color);
+ anim.setInterpolator(interpolator);
+ add(anim);
+ return anim;
+ }
+
+ @Override
+ public <T> Animator setFloat(T target, FloatProperty<T> property, float value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return NO_OP;
+ }
+ Animator anim = ObjectAnimator.ofFloat(target, property, value);
+ anim.setInterpolator(interpolator);
+ add(anim);
+ return anim;
+ }
+
+ @Override
+ public <T> Animator setInt(T target, IntProperty<T> property, int value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return NO_OP;
+ }
+ Animator anim = ObjectAnimator.ofInt(target, property, value);
+ anim.setInterpolator(interpolator);
+ add(anim);
+ return anim;
+ }
+
+
+ /**
+ * Adds a callback to be run on every frame of the animation
+ */
+ public void addOnFrameCallback(Runnable runnable) {
+ addOnFrameListener(anim -> runnable.run());
+ }
+
+ /**
+ * Adds a listener to be run on every frame of the animation
+ */
+ public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
+ if (mProgressAnimator == null) {
+ mProgressAnimator = ValueAnimator.ofFloat(0, 1);
+ }
+
+ mProgressAnimator.addUpdateListener(listener);
+ }
+
+ @Override
+ public void addEndListener(Consumer<Boolean> listener) {
+ if (mProgressAnimator == null) {
+ mProgressAnimator = ValueAnimator.ofFloat(0, 1);
+ }
+ mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
+ }
+
+ /**
+ * @see AnimatorSet#addListener(AnimatorListener)
+ */
+ public void addListener(Animator.AnimatorListener listener) {
+ mAnim.addListener(listener);
+ }
+
+ @Override
+ public void add(Animator a) {
+ mAnim.play(a);
+ }
+
+ /**
+ * Creates and returns the underlying AnimatorSet
+ */
+ @NonNull
+ public AnimatorSet buildAnim() {
+ // Add progress animation to the end, so that frame callback is called after all the other
+ // animation update.
+ if (mProgressAnimator != null) {
+ add(mProgressAnimator);
+ mProgressAnimator = null;
+ }
+ return mAnim;
+ }
+}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 1300ce7..7316420 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -15,24 +15,18 @@
*/
package com.android.launcher3.anim;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.graphics.drawable.ColorDrawable;
import android.util.FloatProperty;
-import android.util.IntProperty;
-import android.view.View;
import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
import java.util.ArrayList;
-import java.util.function.Consumer;
/**
* Utility class to keep track of a running animation.
@@ -43,17 +37,13 @@
*
* TODO: Find a better name
*/
-public class PendingAnimation implements PropertySetter {
+public class PendingAnimation extends AnimatedPropertySetter {
private final ArrayList<Holder> mAnimHolders = new ArrayList<>();
- private final AnimatorSet mAnim;
private final long mDuration;
- private ValueAnimator mProgressAnimator;
-
public PendingAnimation(long duration) {
mDuration = duration;
- mAnim = new AnimatorSet();
}
public long getDuration() {
@@ -68,6 +58,7 @@
add(anim, springProperty);
}
+ @Override
public void add(Animator anim) {
add(anim, SpringProperty.DEFAULT);
}
@@ -84,39 +75,6 @@
mAnim.setInterpolator(interpolator);
}
- @Override
- public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
- if (view == null || view.getAlpha() == alpha) {
- return;
- }
- ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
- anim.addListener(new AlphaUpdateListener(view));
- anim.setInterpolator(interpolator);
- add(anim);
- }
-
- @Override
- public void setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
- if (view == null || (view.getBackground() instanceof ColorDrawable
- && ((ColorDrawable) view.getBackground()).getColor() == color)) {
- return;
- }
- ObjectAnimator anim = ObjectAnimator.ofArgb(view, VIEW_BACKGROUND_COLOR, color);
- anim.setInterpolator(interpolator);
- add(anim);
- }
-
- @Override
- public <T> void setFloat(T target, FloatProperty<T> property, float value,
- TimeInterpolator interpolator) {
- if (property.get(target) == value) {
- return;
- }
- Animator anim = ObjectAnimator.ofFloat(target, property, value);
- anim.setDuration(mDuration).setInterpolator(interpolator);
- add(anim);
- }
-
public <T> void addFloat(T target, FloatProperty<T> property, float from, float to,
TimeInterpolator interpolator) {
Animator anim = ObjectAnimator.ofFloat(target, property, from, to);
@@ -124,57 +82,16 @@
add(anim);
}
- @Override
- public <T> void setInt(T target, IntProperty<T> property, int value,
- TimeInterpolator interpolator) {
- if (property.get(target) == value) {
- return;
- }
- Animator anim = ObjectAnimator.ofInt(target, property, value);
- anim.setInterpolator(interpolator);
- add(anim);
- }
-
- /**
- * Adds a callback to be run on every frame of the animation
- */
- public void addOnFrameCallback(Runnable runnable) {
- addOnFrameListener(anim -> runnable.run());
- }
-
- /**
- * Adds a listener to be run on every frame of the animation
- */
- public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
- if (mProgressAnimator == null) {
- mProgressAnimator = ValueAnimator.ofFloat(0, 1);
- }
-
- mProgressAnimator.addUpdateListener(listener);
- }
-
- /**
- * @see AnimatorSet#addListener(AnimatorListener)
- */
- public void addListener(Animator.AnimatorListener listener) {
- mAnim.addListener(listener);
- }
-
/**
* Creates and returns the underlying AnimatorSet
*/
+ @Override
public AnimatorSet buildAnim() {
- // Add progress animation to the end, so that frame callback is called after all the other
- // animation update.
- if (mProgressAnimator != null) {
- add(mProgressAnimator);
- mProgressAnimator = null;
- }
if (mAnimHolders.isEmpty()) {
// Add a placeholder animation to that the duration is respected
add(ValueAnimator.ofFloat(0, 1).setDuration(mDuration));
}
- return mAnim;
+ return super.buildAnim();
}
/**
@@ -183,14 +100,4 @@
public AnimatorPlaybackController createPlaybackController() {
return new AnimatorPlaybackController(buildAnim(), mDuration, mAnimHolders);
}
-
- /**
- * Add a listener of receiving the success/failure callback in the end.
- */
- public void addEndListener(Consumer<Boolean> listener) {
- if (mProgressAnimator == null) {
- mProgressAnimator = ValueAnimator.ofFloat(0, 1);
- }
- mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
- }
}
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 8d77b4b..d2207f6 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -17,57 +17,94 @@
package com.android.launcher3.anim;
import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
import android.util.FloatProperty;
import android.util.IntProperty;
import android.view.View;
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
/**
* Utility class for setting a property with or without animation
*/
-public interface PropertySetter {
+public abstract class PropertySetter {
- PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter() { };
+ public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter() {
+
+ @Override
+ public void add(Animator animatorSet) {
+ animatorSet.setDuration(0);
+ animatorSet.start();
+ }
+ };
+
+ protected static final AnimatorSet NO_OP = new AnimatorSet();
/**
* Sets the view alpha using the provided interpolator.
* Unlike {@link #setFloat}, this also updates the visibility of the view as alpha changes
* between zero and non-zero.
*/
- default void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ @NonNull
+ public Animator setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
if (view != null) {
view.setAlpha(alpha);
AlphaUpdateListener.updateVisibility(view);
}
+ return NO_OP;
}
/**
* Sets the background color of the provided view using the provided interpolator.
*/
- default void setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
+ @NonNull
+ public Animator setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
if (view != null) {
view.setBackgroundColor(color);
}
+ return NO_OP;
}
/**
* Updates the float property of the target using the provided interpolator
*/
- default <T> void setFloat(T target, FloatProperty<T> property, float value,
+ @NonNull
+ public <T> Animator setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) {
property.setValue(target, value);
+ return NO_OP;
}
/**
* Updates the int property of the target using the provided interpolator
*/
- default <T> void setInt(T target, IntProperty<T> property, int value,
+ @NonNull
+ public <T> Animator setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
property.setValue(target, value);
+ return NO_OP;
}
- default void add(Animator animatorSet) {
- animatorSet.setDuration(0);
- animatorSet.start();
+ /**
+ * Runs the animation as part of setting the property
+ */
+ public abstract void add(Animator animatorSet);
+
+ /**
+ * Add a listener of receiving the success/failure callback in the end.
+ */
+ public void addEndListener(Consumer<Boolean> listener) {
+ listener.accept(true);
+ }
+
+ /**
+ * Creates and returns the AnimatorSet that can be run to apply the properties
+ */
+ @NonNull
+ public AnimatorSet buildAnim() {
+ return NO_OP;
}
}
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 5fac7cf..9e91b9d 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -20,6 +20,7 @@
import static android.os.Process.myUserHandle;
import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
@@ -31,13 +32,18 @@
import android.os.UserHandle;
import android.util.Log;
+import androidx.annotation.AnyThread;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -78,6 +84,7 @@
* Sends a broadcast to all package installers that have items with active sessions on the users
* first screen.
*/
+ @WorkerThread
public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
UserHandle myUser = myUserHandle();
mSessionInfoForPackage
@@ -95,6 +102,7 @@
* @param packages List of packages with active sessions for this package installer.
* @param firstScreenItems List of items on the first screen.
*/
+ @WorkerThread
private void sendBroadcastToInstaller(Context context, String installerPackageName,
Set<String> packages, List<ItemInfo> firstScreenItems) {
Set<String> folderItems = new HashSet<>();
@@ -106,7 +114,7 @@
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
String folderItemInfoPackage;
- for (ItemInfo folderItemInfo : folderInfo.contents) {
+ for (ItemInfo folderItemInfo : cloneOnMainThread(folderInfo.contents)) {
folderItemInfoPackage = getPackageName(folderItemInfo);
if (folderItemInfoPackage != null
&& packages.contains(folderItemInfoPackage)) {
@@ -170,4 +178,17 @@
Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
}
}
+
+ /**
+ * Clone the provided list on UI thread. This is used for {@link FolderInfo#contents} which
+ * is always modified on UI thread.
+ */
+ @AnyThread
+ private static List<WorkspaceItemInfo> cloneOnMainThread(ArrayList<WorkspaceItemInfo> list) {
+ try {
+ return MAIN_EXECUTOR.submit(() -> new ArrayList(list)).get();
+ } catch (Exception e) {
+ return Collections.emptyList();
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/ViewCache.java b/src/com/android/launcher3/util/ViewCache.java
index 08b8744..98e6822 100644
--- a/src/com/android/launcher3/util/ViewCache.java
+++ b/src/com/android/launcher3/util/ViewCache.java
@@ -21,6 +21,8 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.R;
+
/**
* Utility class to cache views at an activity level
*/
@@ -39,18 +41,26 @@
mCache.put(layoutId, entry);
}
+ T result;
if (entry.mCurrentSize > 0) {
entry.mCurrentSize --;
- T result = (T) entry.mViews[entry.mCurrentSize];
+ result = (T) entry.mViews[entry.mCurrentSize];
entry.mViews[entry.mCurrentSize] = null;
- return result;
+ } else {
+ result = (T) LayoutInflater.from(context).inflate(layoutId, parent, false);
+ result.setTag(R.id.cache_entry_tag_id, entry);
}
-
- return (T) LayoutInflater.from(context).inflate(layoutId, parent, false);
+ return result;
}
public void recycleView(int layoutId, View view) {
CacheEntry entry = mCache.get(layoutId);
+ if (entry != view.getTag(R.id.cache_entry_tag_id)) {
+ // Since this view was created, the cache has been reset. The view should not be
+ // recycled since this means the environment could also have changed, requiring new
+ // view setup.
+ return;
+ }
if (entry != null && entry.mCurrentSize < entry.mMaxSize) {
entry.mViews[entry.mCurrentSize] = view;
entry.mCurrentSize++;
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 7a8e9d5..2d6112f 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -287,7 +287,6 @@
case MotionEvent.ACTION_UP:
hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
getApplicationWindowToken());
- break;
case MotionEvent.ACTION_CANCEL:
mRv.onFastScrollCompleted();
mTouchOffsetY = 0;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 1bcba14..8962c4f 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -198,7 +198,9 @@
/** Returns the number of cells that can fit horizontally in a given {@code content}. */
protected int computeMaxHorizontalSpans(View content, int contentHorizontalPaddingPx) {
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
- int availableWidth = content.getMeasuredWidth() - contentHorizontalPaddingPx;
+ int availableWidth = content.getMeasuredWidth()
+ - contentHorizontalPaddingPx
+ - (2 * mContentHorizontalMarginInPx);
Point cellSize = deviceProfile.getCellSize();
if (cellSize.x > 0) {
return availableWidth / cellSize.x;
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 8a97c6b..c93be8b 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -53,7 +53,6 @@
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -199,8 +198,8 @@
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
isWorkspaceScrollable(launcher)));
- assertNull("Chrome app was found on empty workspace",
- workspace.tryGetWorkspaceAppIcon("Chrome"));
+ workspace.verifyWorkspaceAppIconIsGone(
+ "Chrome app was found on empty workspace", "Chrome");
workspace.ensureWorkspaceIsScrollable();
@@ -388,10 +387,10 @@
folder.getAppIcon(GMAIL_APP_NAME);
Workspace workspace = folder.close();
- assertNull(STORE_APP_NAME + " should be moved to a folder.",
- workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME));
- assertNull(GMAIL_APP_NAME + " should be moved to a folder.",
- workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME));
+ workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.",
+ STORE_APP_NAME);
+ workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.",
+ GMAIL_APP_NAME);
final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
folderIcon = mapIcon.dragToIcon(folderIcon);
@@ -399,8 +398,8 @@
folder.getAppIcon(MAPS_APP_NAME);
workspace = folder.close();
- assertNull(MAPS_APP_NAME + " should be moved to a folder.",
- workspace.tryGetWorkspaceAppIcon(MAPS_APP_NAME));
+ workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.",
+ MAPS_APP_NAME);
}
@Test
@@ -424,8 +423,9 @@
for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon);
- assertNull(appName + " app was found after being deleted from workspace",
- workspace.tryGetWorkspaceAppIcon(appName));
+ workspace.verifyWorkspaceAppIconIsGone(
+ appName + " app was found after being deleted from workspace",
+ appName);
}
}
@@ -509,10 +509,8 @@
.containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME);
mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
-
- assertNull(
- DUMMY_APP_NAME + " app was found after being uninstalled",
- mLauncher.getWorkspace().tryGetWorkspaceAppIcon(DUMMY_APP_NAME));
+ mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
+ DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
Map<String, Point> finalPositions =
mLauncher.getWorkspace().getWorkspaceIconsPositions();
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index eb7f05b..e919740 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -140,6 +140,20 @@
}
}
+ /**
+ * Waits for an app icon to be gone (e.g. after uninstall). Fails if it remains.
+ *
+ * @param errorMessage error message thrown then the icon doesn't disappear.
+ * @param appName app that should be gone.
+ */
+ public void verifyWorkspaceAppIconIsGone(String errorMessage, String appName) {
+ final UiObject2 workspace = verifyActiveContainer();
+ assertTrue(errorMessage,
+ workspace.wait(
+ Until.gone(AppIcon.getAppIconSelector(appName, mLauncher)),
+ LauncherInstrumentation.WAIT_TIME_MS));
+ }
+
/**
* Returns an icon for the app; fails if the icon doesn't exist.