summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/folder/Folder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/folder/Folder.java')
-rw-r--r--src/com/android/launcher3/folder/Folder.java302
1 files changed, 199 insertions, 103 deletions
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 93c9ea8e3..80338ca72 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -34,7 +34,6 @@ import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
@@ -46,6 +45,7 @@ import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -60,14 +60,16 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LogDecelerateInterpolator;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.PagedView;
+import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
+import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -75,12 +77,13 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -133,8 +136,10 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
@Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
+ private AnimatorSet mCurrentAnimator;
+
private final int mExpandDuration;
- private final int mMaterialExpandDuration;
+ public final int mMaterialExpandDuration;
private final int mMaterialExpandStagger;
protected final Launcher mLauncher;
@@ -501,51 +506,31 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
mState = STATE_SMALL;
}
- /**
- * Opens the user folder described by the specified tag. The opening of the folder
- * is animated relative to the specified View. If the View is null, no animation
- * is played.
- */
- public void animateOpen() {
- Folder openFolder = getOpen(mLauncher);
- if (openFolder != null && openFolder != this) {
- // Close any open folder before opening a folder.
- openFolder.close(true);
+ private void startAnimation(final AnimatorSet a) {
+ if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.cancel();
}
-
- DragLayer dragLayer = mLauncher.getDragLayer();
- // Just verify that the folder hasn't already been added to the DragLayer.
- // There was a one-off crash where the folder had a parent already.
- if (getParent() == null) {
- dragLayer.addView(this);
- mDragController.addDropTarget(this);
- } else {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
- + getParent());
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mState = STATE_ANIMATING;
+ mCurrentAnimator = a;
}
- }
-
- mIsOpen = true;
-
- mContent.completePendingPageChanges();
- if (!mDragInProgress) {
- // Open on the first page.
- mContent.snapToPageImmediately(0);
- }
- // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
- // leads to an inconsistent state if you drag out of the folder and drag back in without
- // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
- mDeleteFolderOnDropCompleted = false;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimator = null;
+ }
+ });
+ a.start();
+ }
- final Runnable onCompleteRunnable;
+ private AnimatorSet getOpeningAnimator() {
prepareReveal();
- centerAboutIcon();
-
mFolderIcon.growAndFadeOut();
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+
int width = getFolderWidth();
int height = getFolderHeight();
@@ -587,24 +572,76 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
anim.play(textAlpha);
anim.play(reveal);
- mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
- mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
+ AnimationLayerSet layerSet = new AnimationLayerSet();
+ layerSet.addView(mContent);
+ layerSet.addView(mFooter);
+ anim.addListener(layerSet);
+
+ return anim;
+ }
+
+ /**
+ * Opens the user folder described by the specified tag. The opening of the folder
+ * is animated relative to the specified View. If the View is null, no animation
+ * is played.
+ */
+ public void animateOpen() {
+ Folder openFolder = getOpen(mLauncher);
+ if (openFolder != null && openFolder != this) {
+ // Close any open folder before opening a folder.
+ openFolder.close(true);
+ }
+
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ // Just verify that the folder hasn't already been added to the DragLayer.
+ // There was a one-off crash where the folder had a parent already.
+ if (getParent() == null) {
+ dragLayer.addView(this);
+ mDragController.addDropTarget(this);
+ } else {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ + getParent());
+ }
+ }
+
+ mIsOpen = true;
+
+ mContent.completePendingPageChanges();
+ if (!mDragInProgress) {
+ // Open on the first page.
+ mContent.snapToPageImmediately(0);
+ }
+
+ // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+ // leads to an inconsistent state if you drag out of the folder and drag back in without
+ // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+ mDeleteFolderOnDropCompleted = false;
+
+ final Runnable onCompleteRunnable;
+ centerAboutIcon();
+
+ AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ ? new FolderAnimationManager(this, true /* isOpening */).getAnimator()
+ : getOpeningAnimator();
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mContent.setLayerType(LAYER_TYPE_NONE, null);
- mFooter.setLayerType(LAYER_TYPE_NONE, null);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
};
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.drawLeaveBehindIfExists();
+ mFolderIcon.setVisibility(INVISIBLE);
+ }
+
Utilities.sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
- mState = STATE_ANIMATING;
}
@Override
public void onAnimationEnd(Animator animation) {
@@ -650,7 +687,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
mPageIndicator.stopAllAnimations();
- anim.start();
+ startAnimation(anim);
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
@@ -689,7 +726,11 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
if (mFolderIcon != null) {
- mFolderIcon.shrinkAndFadeIn(animate);
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.clearLeaveBehindIfExists();
+ } else {
+ mFolderIcon.shrinkAndFadeIn(animate);
+ }
}
if (!(getParent() instanceof DragLayer)) return;
@@ -706,12 +747,24 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
+ private AnimatorSet getClosingAnimator() {
+ AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
+ animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f));
+
+ AnimationLayerSet layerSet = new AnimationLayerSet();
+ layerSet.addView(this);
+ animatorSet.addListener(layerSet);
+ animatorSet.setDuration(mExpandDuration);
+ return animatorSet;
+ }
+
private void animateClosed() {
- final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
- oa.addListener(new AnimatorListenerAdapter() {
+ AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ ? new FolderAnimationManager(this, false /* isOpening */).getAnimator()
+ : getClosingAnimator();
+ a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- setLayerType(LAYER_TYPE_NONE, null);
closeComplete(true);
}
@Override
@@ -720,12 +773,9 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
- mState = STATE_ANIMATING;
}
});
- oa.setDuration(mExpandDuration);
- setLayerType(LAYER_TYPE_HARDWARE, null);
- oa.start();
+ startAnimation(a);
}
private void closeComplete(boolean wasAnimated) {
@@ -736,8 +786,12 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
mDragController.removeDropTarget(this);
clearFocus();
- if (wasAnimated) {
- mFolderIcon.requestFocus();
+ if (mFolderIcon != null) {
+ mFolderIcon.setVisibility(View.VISIBLE);
+ if (wasAnimated) {
+ mFolderIcon.mBackground.animateBackgroundStroke();
+ mFolderIcon.requestFocus();
+ }
}
if (mRearrangeOnClose) {
@@ -1027,6 +1081,9 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
public boolean isDropEnabled() {
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ return mState != STATE_ANIMATING;
+ }
return true;
}
@@ -1288,53 +1345,68 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
mContent.completePendingPageChanges();
- View currentDragView;
- final ShortcutInfo si;
- if (d.dragInfo instanceof AppInfo) {
- // Came from all apps -- make a copy.
- si = ((AppInfo) d.dragInfo).makeShortcut();
+ if (d.dragInfo instanceof PendingAddShortcutInfo) {
+ PendingAddShortcutInfo pasi = (PendingAddShortcutInfo) d.dragInfo;
+ pasi.container = mInfo.id;
+ pasi.rank = mEmptyCellRank;
+
+ mLauncher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
+ pasi.spanY);
+ d.deferDragViewCleanupPostAnimation = false;
+ mRearrangeOnClose = true;
} else {
- // ShortcutInfo
- si = (ShortcutInfo) d.dragInfo;
- }
- if (mIsExternalDrag) {
- currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
- // Actually move the item in the database if it was an external drag. Call this
- // before creating the view, so that ShortcutInfo is updated appropriately.
- mLauncher.getModelWriter().addOrMoveItemInDatabase(
- si, mInfo.id, 0, si.cellX, si.cellY);
-
- // We only need to update the locations if it doesn't get handled in #onDropCompleted.
- if (d.dragSource != this) {
- updateItemLocationsInDatabaseBatch();
+ final ShortcutInfo si;
+ if (d.dragInfo instanceof AppInfo) {
+ // Came from all apps -- make a copy.
+ si = ((AppInfo) d.dragInfo).makeShortcut();
+ } else {
+ // ShortcutInfo
+ si = (ShortcutInfo) d.dragInfo;
}
- mIsExternalDrag = false;
- } else {
- currentDragView = mCurrentDragView;
- mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
- }
- if (d.dragView.hasDrawn()) {
+ View currentDragView;
+ if (mIsExternalDrag) {
+ currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
- // Temporarily reset the scale such that the animation target gets calculated correctly.
- float scaleX = getScaleX();
- float scaleY = getScaleY();
- setScaleX(1.0f);
- setScaleY(1.0f);
- mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
- cleanUpRunnable, null);
- setScaleX(scaleX);
- setScaleY(scaleY);
- } else {
- d.deferDragViewCleanupPostAnimation = false;
- currentDragView.setVisibility(VISIBLE);
- }
- mItemsInvalidated = true;
- rearrangeChildren();
+ // Actually move the item in the database if it was an external drag. Call this
+ // before creating the view, so that ShortcutInfo is updated appropriately.
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(
+ si, mInfo.id, 0, si.cellX, si.cellY);
+
+ // We only need to update the locations if it doesn't get handled in
+ // #onDropCompleted.
+ if (d.dragSource != this) {
+ updateItemLocationsInDatabaseBatch();
+ }
+ mIsExternalDrag = false;
+ } else {
+ currentDragView = mCurrentDragView;
+ mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+ }
+
+ if (d.dragView.hasDrawn()) {
+ // Temporarily reset the scale such that the animation target gets calculated
+ // correctly.
+ float scaleX = getScaleX();
+ float scaleY = getScaleY();
+ setScaleX(1.0f);
+ setScaleY(1.0f);
+ mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
+ cleanUpRunnable, null);
+ setScaleX(scaleX);
+ setScaleY(scaleY);
+ } else {
+ d.deferDragViewCleanupPostAnimation = false;
+ currentDragView.setVisibility(VISIBLE);
+ }
+
+ mItemsInvalidated = true;
+ rearrangeChildren();
- // Temporarily suppress the listener, as we did all the work already here.
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, false);
+ // Temporarily suppress the listener, as we did all the work already here.
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.add(si, false);
+ }
}
// Clear the drag info, as it is no longer being dragged.
@@ -1363,11 +1435,15 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
@Override
- public void onAdd(ShortcutInfo item) {
- mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
+ public void onAdd(ShortcutInfo item, int rank) {
+ View view = mContent.createAndAddViewForRank(item, rank);
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
+ item.cellY);
+
+ ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
+ items.add(rank, view);
+ mContent.arrangeChildren(items, items.size());
mItemsInvalidated = true;
- mLauncher.getModelWriter().addOrMoveItemInDatabase(
- item, mInfo.id, 0, item.cellX, item.cellY);
}
public void onRemove(ShortcutInfo item) {
@@ -1427,6 +1503,26 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
return mItemsInReadingOrder;
}
+ public List<BubbleTextView> getItemsOnCurrentPage() {
+ ArrayList<View> allItems = getItemsInReadingOrder();
+ int currentPage = mContent.getCurrentPage();
+ int lastPage = mContent.getPageCount() - 1;
+ int totalItemsInFolder = allItems.size();
+ int itemsPerPage = mContent.itemsPerPage();
+ int numItemsOnCurrentPage = currentPage == lastPage
+ ? totalItemsInFolder - (itemsPerPage * currentPage)
+ : itemsPerPage;
+
+ int startIndex = currentPage * itemsPerPage;
+ int endIndex = startIndex + numItemsOnCurrentPage;
+
+ List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
+ for (int i = startIndex; i < endIndex; ++i) {
+ itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
+ }
+ return itemsOnCurrentPage;
+ }
+
public void onFocusChange(View v, boolean hasFocus) {
if (v == mFolderName) {
if (hasFocus) {