Merge "Moving mState from Workspace to StateManager" into ub-launcher3-master
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index cd72fba..364b204 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -178,6 +178,15 @@
}
+ /**
+ * Resets the view so it can be recycled.
+ */
+ public void reset() {
+ mBadgeInfo = null;
+ mBadgePalette = null;
+ mForceHideBadge = false;
+ }
+
public void applyFromShortcutInfo(ShortcutInfo info) {
applyFromShortcutInfo(info, false);
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index bebdbdb..6f40408 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -201,6 +201,10 @@
protected abstract boolean supportsDrop(ItemInfo info);
+ public boolean supportsAccessibilityDrop(ItemInfo info) {
+ return supportsDrop(info);
+ }
+
@Override
public boolean isDropEnabled() {
return mActive && (mAccessibleDrag ||
@@ -241,9 +245,13 @@
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
+ public abstract int getAccessibilityAction();
+
@Override
public void prepareAccessibilityDrop() { }
+ public abstract void onAccessibilityDrop(View view, ItemInfo item);
+
public abstract void completeDrop(DragObject d);
@Override
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index fdd4f34..c12ea57 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -21,6 +21,7 @@
import android.util.AttributeSet;
import android.view.View;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
@@ -49,14 +50,22 @@
setTextBasedOnDragSource(dragObject.dragInfo);
}
- /** @return true for items that should have a "Remove" action in accessibility. */
- public static boolean supportsAccessibleDrop(ItemInfo info) {
+ /**
+ * @return true for items that should have a "Remove" action in accessibility.
+ */
+ @Override
+ public boolean supportsAccessibilityDrop(ItemInfo info) {
return (info instanceof ShortcutInfo)
|| (info instanceof LauncherAppWidgetInfo)
|| (info instanceof FolderInfo);
}
@Override
+ public int getAccessibilityAction() {
+ return LauncherAccessibilityDelegate.REMOVE;
+ }
+
+ @Override
protected boolean supportsDrop(ItemInfo info) {
return true;
}
@@ -77,19 +86,21 @@
public void completeDrop(DragObject d) {
ItemInfo item = d.dragInfo;
if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
- removeWorkspaceOrFolderItem(mLauncher, item, null);
+ onAccessibilityDrop(null, item);
}
}
/**
* Removes the item from the workspace. If the view is not null, it also removes the view.
*/
- public static void removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
+ @Override
+ public void onAccessibilityDrop(View view, ItemInfo item) {
// Remove the item from launcher and the db, we can ignore the containerInfo in this call
// because we already remove the drag view from the folder (if the drag originated from
// a folder) in Folder.beginDrag()
- launcher.removeItem(view, item, true /* deleteFromDb */);
- launcher.getWorkspace().stripEmptyScreens();
- launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed));
+ mLauncher.removeItem(view, item, true /* deleteFromDb */);
+ mLauncher.getWorkspace().stripEmptyScreens();
+ mLauncher.getDragLayer()
+ .announceForAccessibility(getContext().getString(R.string.item_removed));
}
}
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 29a1349..2f8374a 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.AlphaUpdateListener.updateVisibility;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+
import android.animation.TimeInterpolator;
import android.content.Context;
import android.util.AttributeSet;
@@ -23,13 +26,14 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.widget.LinearLayout;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
+import java.util.ArrayList;
+
/*
* The top bar containing various drop targets: Delete/App Info/Uninstall.
*/
@@ -42,10 +46,7 @@
@Override
public void run() {
- AccessibilityManager am = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- boolean accessibilityEnabled = am.isEnabled();
- AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled);
+ updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
}
};
@@ -55,6 +56,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
protected boolean mVisible = false;
+ private ButtonDropTarget[] mDropTargets;
private ViewPropertyAnimator mCurrentAnimation;
public DropTargetBar(Context context, AttributeSet attrs) {
@@ -75,7 +77,27 @@
public void setup(DragController dragController) {
dragController.addDragListener(this);
- setupButtonDropTarget(this, dragController);
+ ArrayList<ButtonDropTarget> outList = new ArrayList<>();
+ findDropTargets(this, outList);
+
+ mDropTargets = new ButtonDropTarget[outList.size()];
+ for (int i = 0; i < mDropTargets.length; i++) {
+ mDropTargets[i] = outList.get(i);
+ mDropTargets[i].setDropTargetBar(this);
+ dragController.addDragListener(mDropTargets[i]);
+ dragController.addDropTarget(mDropTargets[i]);
+ }
+ }
+
+ private static void findDropTargets(View view, ArrayList<ButtonDropTarget> outTargets) {
+ if (view instanceof ButtonDropTarget) {
+ outTargets.add((ButtonDropTarget) view);
+ } else if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+ for (int i = vg.getChildCount() - 1; i >= 0; i--) {
+ findDropTargets(vg.getChildAt(i), outTargets);
+ }
+ }
}
@Override
@@ -130,20 +152,6 @@
return result;
}
- private void setupButtonDropTarget(View view, DragController dragController) {
- if (view instanceof ButtonDropTarget) {
- ButtonDropTarget bdt = (ButtonDropTarget) view;
- bdt.setDropTargetBar(this);
- dragController.addDragListener(bdt);
- dragController.addDropTarget(bdt);
- } else if (view instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) view;
- for (int i = vg.getChildCount() - 1; i >= 0; i--) {
- setupButtonDropTarget(vg.getChildAt(i), dragController);
- }
- }
- }
-
private void animateToVisibility(boolean isVisible) {
if (mVisible != isVisible) {
mVisible = isVisible;
@@ -190,4 +198,8 @@
mDeferOnDragEnd = false;
}
}
+
+ public ButtonDropTarget[] getDropTargets() {
+ return mDropTargets;
+ }
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f78cde5..289242f 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -26,6 +26,7 @@
import android.util.Log;
import android.widget.Toast;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.util.Themes;
@@ -49,8 +50,8 @@
}
@Override
- protected ComponentName performDropAction(DragObject d) {
- return performDropAction(mLauncher, d.dragInfo, null, null);
+ protected ComponentName performDropAction(ItemInfo item) {
+ return performDropAction(mLauncher, item, null, null);
}
/**
@@ -96,13 +97,15 @@
}
@Override
- protected boolean supportsDrop(ItemInfo info) {
- return supportsDrop(getContext(), info);
+ public int getAccessibilityAction() {
+ return LauncherAccessibilityDelegate.INFO;
}
- public static boolean supportsDrop(Context context, ItemInfo info) {
+ @Override
+ protected boolean supportsDrop(ItemInfo info) {
// Only show the App Info drop target if developer settings are enabled.
- boolean developmentSettingsEnabled = Settings.Global.getInt(context.getContentResolver(),
+ boolean developmentSettingsEnabled = Settings.Global.getInt(
+ getContext().getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
if (!developmentSettingsEnabled) {
return false;
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
index fc75fe3..d1a2538 100644
--- a/src/com/android/launcher3/PinchToOverviewListener.java
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -16,30 +16,22 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.util.Range;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import com.android.launcher3.compat.AnimatorSetCompat;
import com.android.launcher3.util.TouchController;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* Detects pinches and animates the Workspace to/from overview mode.
*/
-@TargetApi(Build.VERSION_CODES.O)
public class PinchToOverviewListener
implements TouchController, OnScaleGestureListener, Runnable {
@@ -55,9 +47,8 @@
private Workspace mWorkspace = null;
private boolean mPinchStarted = false;
- private AnimatorSet mCurrentAnimation;
+ private AnimatorSetCompat mCurrentAnimation;
private float mCurrentScale;
- private Range<Integer> mDurationRange;
private boolean mShouldGoToFinalState;
private LauncherState mToState;
@@ -109,14 +100,13 @@
}
mToState = mLauncher.isInState(OVERVIEW) ? NORMAL : OVERVIEW;
- mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, this);
+ mCurrentAnimation = AnimatorSetCompat.wrap(mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, this), OVERVIEW_TRANSITION_MS);
mPinchStarted = true;
mCurrentScale = 1;
- mDurationRange = Range.create(0, LauncherAnimUtils.OVERVIEW_TRANSITION_MS);
mShouldGoToFinalState = false;
- dispatchOnStart(mCurrentAnimation);
+ mCurrentAnimation.dispatchOnStart();
return true;
}
@@ -160,26 +150,7 @@
}
// Move the transition animation to that duration.
- long playPosition = mDurationRange.clamp(
- (int) ((1 - animationFraction) * mDurationRange.getUpper()));
- mCurrentAnimation.setCurrentPlayTime(playPosition);
-
+ mCurrentAnimation.setPlayFraction(1 - animationFraction);
return true;
}
-
- private void dispatchOnStart(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
- l.onAnimationStart(animator);
- }
-
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnStart(anim);
- }
- }
- }
-
- private static <T> List<T> nonNullList(ArrayList<T> list) {
- return list == null ? Collections.<T>emptyList() : list;
- }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 4393819..8e83a30 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -10,22 +10,28 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import java.net.URISyntaxException;
-public class UninstallDropTarget extends ButtonDropTarget {
+public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmListener {
private static final String TAG = "UninstallDropTarget";
- private static Boolean sUninstallDisabled;
+
+ private static final long CACHE_EXPIRE_TIMEOUT = 5000;
+ private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
+
+ private final Alarm mCacheExpireAlarm;
public UninstallDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -33,6 +39,9 @@
public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ mCacheExpireAlarm = new Alarm();
+ mCacheExpireAlarm.setOnAlarmListener(this);
}
@Override
@@ -48,18 +57,29 @@
}
@Override
- protected boolean supportsDrop(ItemInfo info) {
- return supportsDrop(getContext(), info);
+ public void onAlarm(Alarm alarm) {
+ mUninstallDisabledCache.clear();
}
- public static boolean supportsDrop(Context context, ItemInfo info) {
- if (sUninstallDisabled == null) {
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- Bundle restrictions = userManager.getUserRestrictions();
- sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+ @Override
+ public int getAccessibilityAction() {
+ return LauncherAccessibilityDelegate.UNINSTALL;
+ }
+
+ @Override
+ protected boolean supportsDrop(ItemInfo info) {
+ Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user);
+ if (uninstallDisabled == null) {
+ UserManager userManager =
+ (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ Bundle restrictions = userManager.getUserRestrictions(info.user);
+ uninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
|| restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false);
+ mUninstallDisabledCache.put(info.user, uninstallDisabled);
}
- if (sUninstallDisabled) {
+ // Cancel any pending alarm and set cache expiry after some time
+ mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
+ if (uninstallDisabled) {
return false;
}
@@ -69,13 +89,13 @@
return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0;
}
}
- return getUninstallTarget(context, info) != null;
+ return getUninstallTarget(info) != null;
}
/**
* @return the component name that should be uninstalled or null.
*/
- private static ComponentName getUninstallTarget(Context context, ItemInfo item) {
+ private ComponentName getUninstallTarget(ItemInfo item) {
Intent intent = null;
UserHandle user = null;
if (item != null &&
@@ -84,7 +104,7 @@
user = item.user;
}
if (intent != null) {
- LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
+ LauncherActivityInfo info = LauncherAppsCompat.getInstance(mLauncher)
.resolveActivity(intent, user);
if (info != null
&& (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -103,7 +123,7 @@
@Override
public void completeDrop(final DragObject d) {
- ComponentName target = performDropAction(d);
+ ComponentName target = performDropAction(d.dragInfo);
if (d.dragSource instanceof DeferredOnComplete) {
DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
if (target != null) {
@@ -119,27 +139,19 @@
* Performs the drop action and returns the target component for the dragObject or null if
* the action was not performed.
*/
- protected ComponentName performDropAction(DragObject d) {
- return performDropAction(mLauncher, d.dragInfo);
- }
-
- /**
- * Performs the drop action and returns the target component for the dragObject or null if
- * the action was not performed.
- */
- private static ComponentName performDropAction(Context context, ItemInfo info) {
- ComponentName cn = getUninstallTarget(context, info);
+ protected ComponentName performDropAction(ItemInfo info) {
+ ComponentName cn = getUninstallTarget(info);
if (cn == null) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
- Toast.makeText(context, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+ Toast.makeText(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
return null;
}
try {
- Intent i = Intent.parseUri(context.getString(R.string.delete_package_intent), 0)
+ Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0)
.setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
.putExtra(Intent.EXTRA_USER, info.user);
- context.startActivity(i);
+ mLauncher.startActivity(i);
return cn;
} catch (URISyntaxException e) {
Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
@@ -147,8 +159,9 @@
}
}
- public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
- return performDropAction(launcher, info) != null;
+ @Override
+ public void onAccessibilityDrop(View view, ItemInfo item) {
+ performDropAction(item);
}
/**
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2b3a113..512db72 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -19,6 +19,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DropTarget.DragObject;
@@ -48,9 +49,9 @@
private static final String TAG = "LauncherAccessibilityDelegate";
- protected static final int REMOVE = R.id.action_remove;
- protected static final int INFO = R.id.action_info;
- protected static final int UNINSTALL = R.id.action_uninstall;
+ public static final int REMOVE = R.id.action_remove;
+ public static final int INFO = R.id.action_info;
+ public static final int UNINSTALL = R.id.action_uninstall;
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;
@@ -111,14 +112,10 @@
info.addAction(mActions.get(DEEP_SHORTCUTS));
}
- if (DeleteDropTarget.supportsAccessibleDrop(item)) {
- info.addAction(mActions.get(REMOVE));
- }
- if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
- info.addAction(mActions.get(UNINSTALL));
- }
- if (InfoDropTarget.supportsDrop(host.getContext(), item)) {
- info.addAction(mActions.get(INFO));
+ for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
+ if (target.supportsAccessibilityDrop(item)) {
+ info.addAction(mActions.get(target.getAccessibilityAction()));
+ }
}
// Do not add move actions for keyboard request as this uses virtual nodes.
@@ -151,15 +148,7 @@
}
public boolean performAction(final View host, final ItemInfo item, int action) {
- if (action == REMOVE) {
- DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
- return true;
- } else if (action == INFO) {
- InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null, null);
- return true;
- } else if (action == UNINSTALL) {
- return UninstallDropTarget.startUninstallActivity(mLauncher, item);
- } else if (action == MOVE) {
+ if (action == MOVE) {
beginAccessibleDrag(host, item);
} else if (action == ADD_TO_WORKSPACE) {
final int[] coordinates = new int[2];
@@ -234,6 +223,13 @@
return true;
} else if (action == DEEP_SHORTCUTS) {
return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
+ } else {
+ for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
+ if (action == dropTarget.getAccessibilityAction()) {
+ dropTarget.onAccessibilityDrop(host, item);
+ return true;
+ }
+ }
}
return false;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index f7ce8c1..ed5bf9f 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -336,6 +336,7 @@
case VIEW_TYPE_PREDICTION_ICON:
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
+ icon.reset();
icon.applyFromApplicationInfo(info);
break;
case VIEW_TYPE_DISCOVERY_ITEM:
diff --git a/src/com/android/launcher3/compat/AnimatorSetCompat.java b/src/com/android/launcher3/compat/AnimatorSetCompat.java
new file mode 100644
index 0000000..497dd14
--- /dev/null
+++ b/src/com/android/launcher3/compat/AnimatorSetCompat.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.compat;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.animation.LinearInterpolator;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Compat implementation for various new APIs in {@link AnimatorSet}
+ *
+ * Note: The compat implementation does not support start delays on child animations or
+ * sequential playbacks.
+ */
+public abstract class AnimatorSetCompat implements ValueAnimator.AnimatorUpdateListener {
+
+ public static AnimatorSetCompat wrap(AnimatorSet anim, int duration) {
+ if (Utilities.ATLEAST_OREO) {
+ return new AnimatorSetCompatVO(anim, duration);
+ } else {
+ return new AnimatorSetCompatVL(anim, duration);
+ }
+ }
+
+ private final ValueAnimator mAnimationPlayer;
+ private final long mDuration;
+
+ protected final AnimatorSet mAnim;
+
+ protected float mCurrentFraction;
+
+ protected AnimatorSetCompat(AnimatorSet anim, int duration) {
+ mAnim = anim;
+ mDuration = duration;
+
+ mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
+ mAnimationPlayer.setInterpolator(new LinearInterpolator());
+ mAnimationPlayer.addUpdateListener(this);
+ }
+
+ /**
+ * Starts playing the animation forward from current position.
+ */
+ public void start() {
+ mAnimationPlayer.setFloatValues(mCurrentFraction, 1);
+ mAnimationPlayer.setDuration(clampDuration(1 - mCurrentFraction));
+ mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
+ mAnimationPlayer.start();
+ }
+
+ /**
+ * Starts playing the animation backwards from current position
+ */
+ public void reverse() {
+ mAnimationPlayer.setFloatValues(mCurrentFraction, 0);
+ mAnimationPlayer.setDuration(clampDuration(mCurrentFraction));
+ mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
+ mAnimationPlayer.start();
+ }
+
+ /**
+ * Sets the current animation position and updates all the child animators accordingly.
+ */
+ public abstract void setPlayFraction(float fraction);
+
+ /**
+ * @see Animator#addListener(AnimatorListener)
+ */
+ public void addListener(Animator.AnimatorListener listener) {
+ mAnimationPlayer.addListener(listener);
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ setPlayFraction((float) valueAnimator.getAnimatedValue());
+ }
+
+ protected long clampDuration(float fraction) {
+ float playPos = mDuration * fraction;
+ if (playPos <= 0) {
+ return 0;
+ } else {
+ return Math.min((long) playPos, mDuration);
+ }
+ }
+
+ public void dispatchOnStart() {
+ dispatchOnStartRecursively(mAnim);
+ }
+
+ private void dispatchOnStartRecursively(Animator animator) {
+ for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ l.onAnimationStart(animator);
+ }
+
+ if (animator instanceof AnimatorSet) {
+ for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
+ dispatchOnStartRecursively(anim);
+ }
+ }
+ }
+
+ public static class AnimatorSetCompatVL extends AnimatorSetCompat {
+
+ private final ValueAnimator[] mChildAnimations;
+
+ private AnimatorSetCompatVL(AnimatorSet anim, int duration) {
+ super(anim, duration);
+
+ // Build animation list
+ ArrayList<ValueAnimator> childAnims = new ArrayList<>();
+ getAnimationsRecur(mAnim, childAnims);
+ mChildAnimations = childAnims.toArray(new ValueAnimator[childAnims.size()]);
+ }
+
+ private void getAnimationsRecur(AnimatorSet anim, ArrayList<ValueAnimator> out) {
+ long forceDuration = anim.getDuration();
+ for (Animator child : anim.getChildAnimations()) {
+ if (forceDuration > 0) {
+ child.setDuration(forceDuration);
+ }
+ if (child instanceof ValueAnimator) {
+ out.add((ValueAnimator) child);
+ } else if (child instanceof AnimatorSet) {
+ getAnimationsRecur((AnimatorSet) child, out);
+ } else {
+ throw new RuntimeException("Unknown animation type " + child);
+ }
+ }
+ }
+
+ @Override
+ public void setPlayFraction(float fraction) {
+ mCurrentFraction = fraction;
+ long playPos = clampDuration(fraction);
+ for (ValueAnimator anim : mChildAnimations) {
+ anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
+ }
+ }
+
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ private static class AnimatorSetCompatVO extends AnimatorSetCompat {
+
+ private AnimatorSetCompatVO(AnimatorSet anim, int duration) {
+ super(anim, duration);
+ }
+
+ @Override
+ public void setPlayFraction(float fraction) {
+ mCurrentFraction = fraction;
+ mAnim.setCurrentPlayTime(clampDuration(fraction));
+ }
+ }
+
+ private class OnAnimationEndDispatcher extends AnimationSuccessListener {
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ dispatchOnEndRecursively(mAnim);
+ }
+
+ private void dispatchOnEndRecursively(Animator animator) {
+ for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ l.onAnimationEnd(animator);
+ }
+
+ if (animator instanceof AnimatorSet) {
+ for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
+ dispatchOnEndRecursively(anim);
+ }
+ }
+ }
+ }
+
+ private static <T> List<T> nonNullList(ArrayList<T> list) {
+ return list == null ? Collections.<T>emptyList() : list;
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index f2bad6b..bc5aafc 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -143,7 +143,7 @@
public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) {
mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled
- || !Utilities.ATLEAST_OREO ? null : new PinchToOverviewListener(mLauncher);
+ ? null : new PinchToOverviewListener(mLauncher);
}
public boolean isEventOverHotseat(MotionEvent ev) {