OSDN Git Service

Implement spring-loading of folders when dragging over.
authorJorim Jaggi <jjaggi@google.com>
Thu, 16 Jan 2014 23:30:42 +0000 (15:30 -0800)
committerJorim Jaggi <jjaggi@google.com>
Wed, 12 Feb 2014 21:16:51 +0000 (22:16 +0100)
Bug: 8912132
Change-Id: Id81889a133e56461df2e20599c4b40020818ba18

src/com/android/launcher3/AppsCustomizePagedView.java
src/com/android/launcher3/DragLayer.java
src/com/android/launcher3/Folder.java
src/com/android/launcher3/FolderIcon.java
src/com/android/launcher3/Launcher.java

index 49b12b1..251ae21 100644 (file)
@@ -817,7 +817,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
      */
     private void endDragging(View target, boolean isFlingToDelete, boolean success) {
         if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
-                !(target instanceof DeleteDropTarget))) {
+                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
             // Exit spring loaded mode if we have not successfully dropped or have not handled the
             // drop in Workspace
             mLauncher.getWorkspace().removeExtraEmptyScreen(true, new Runnable() {
index ab0469d..862ceca 100644 (file)
@@ -65,8 +65,6 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
 
     private boolean mHoverPointClosesFolder = false;
     private Rect mHitRect = new Rect();
-    private int mWorkspaceIndex = -1;
-    private int mQsbIndex = -1;
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_FADE_OUT = 1;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
@@ -75,6 +73,8 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
 
     private final Rect mInsets = new Rect();
 
+    private int mDragViewIndex;
+
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -771,31 +771,26 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
     }
 
     private void updateChildIndices() {
-        if (mLauncher != null) {
-            mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace());
-            mQsbIndex = indexOfChild(mLauncher.getSearchBar());
+        mDragViewIndex = -1;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            if (getChildAt(i) instanceof DragView) {
+                mDragViewIndex = i;
+            }
         }
     }
 
     @Override
     protected int getChildDrawingOrder(int childCount, int i) {
-        // TODO: We have turned off this custom drawing order because it now effects touch
-        // dispatch order. We need to sort that issue out and then decide how to go about this.
-        if (true || LauncherAppState.isScreenLandscape(getContext()) ||
-                mWorkspaceIndex == -1 || mQsbIndex == -1 ||
-                mLauncher.getWorkspace().isDrawingBackgroundGradient()) {
+        if (mDragViewIndex == -1) {
             return i;
-        }
-
-        // This ensures that the workspace is drawn above the hotseat and qsb,
-        // except when the workspace is drawing a background gradient, in which
-        // case we want the workspace to stay behind these elements.
-        if (i == mQsbIndex) {
-            return mWorkspaceIndex;
-        } else if (i == mWorkspaceIndex) {
-            return mQsbIndex;
-        } else {
+        } else if (i == mDragViewIndex) {
+            return getChildCount()-1;
+        } else if (i < mDragViewIndex) {
             return i;
+        } else {
+            // i > mDragViewIndex
+            return i-1;
         }
     }
 
index a9134e1..b4c3992 100644 (file)
@@ -24,7 +24,6 @@ import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
 import android.support.v4.widget.AutoScrollHelper;
 import android.text.InputType;
@@ -70,6 +69,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
     static final int STATE_ANIMATING = 1;
     static final int STATE_OPEN = 2;
 
+    private static final int CLOSE_FOLDER_DELAY_MS = 150;
+
     private int mExpandDuration;
     protected CellLayout mContent;
     private ScrollView mScrollView;
@@ -85,10 +86,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
     private int mMaxCountY;
     private int mMaxNumItems;
     private ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
-    private Drawable mIconDrawable;
     boolean mItemsInvalidated = false;
     private ShortcutInfo mCurrentDragInfo;
     private View mCurrentDragView;
+    private boolean mIsExternalDrag;
     boolean mSuppressOnAdd = false;
     private int[] mTargetCell = new int[2];
     private int[] mPreviousTargetCell = new int[2];
@@ -242,7 +243,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
 
             mLauncher.getWorkspace().onDragStartedWithItem(v);
             mLauncher.getWorkspace().beginDragShared(v, this);
-            mIconDrawable = ((TextView) v).getCompoundDrawables()[1];
 
             mCurrentDragInfo = item;
             mEmptyCell[0] = item.cellX;
@@ -303,10 +303,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
         return mFolderName;
     }
 
-    public Drawable getDragDrawable() {
-        return mIconDrawable;
-    }
-
     /**
      * We need to handle touch events to prevent them from falling through to the workspace below.
      */
@@ -381,7 +377,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
         int count = 0;
         for (int i = 0; i < children.size(); i++) {
             ShortcutInfo child = (ShortcutInfo) children.get(i);
-            if (!createAndAddShortcut(child)) {
+            if (createAndAddShortcut(child) == null) {
                 overflow.add(child);
             } else {
                 count++;
@@ -466,11 +462,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
                 setLayerType(LAYER_TYPE_NONE, null);
-                Cling cling = mLauncher.getLauncherClings().showFoldersCling();
-                if (cling != null) {
-                    cling.bringScrimToFront();
-                    bringToFront();
-                    cling.bringToFront();
+
+                // Only show cling if we are not in the middle of a drag - this would be quite jarring.
+                if (!mDragController.isDragging()) {
+                    Cling cling = mLauncher.getLauncherClings().showFoldersCling();
+                    if (cling != null) {
+                        cling.bringScrimToFront();
+                        bringToFront();
+                        cling.bringToFront();
+                    }
                 }
                 setFocusOnFirstChild();
             }
@@ -478,6 +478,23 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
         oa.setDuration(mExpandDuration);
         setLayerType(LAYER_TYPE_HARDWARE, null);
         oa.start();
+
+        // Make sure the folder picks up the last drag move even if the finger doesn't move.
+        if (mDragController.isDragging()) {
+            mDragController.forceTouchMove();
+        }
+    }
+
+    public void beginExternalDrag(ShortcutInfo item) {
+        setupContentForNumItems(getItemCount() + 1);
+        findAndSetEmptyCells(item);
+
+        mCurrentDragInfo = item;
+        mEmptyCell[0] = item.cellX;
+        mEmptyCell[1] = item.cellY;
+        mIsExternalDrag = true;
+
+        mDragInProgress = true;
     }
 
     private void sendCustomAccessibilityEvent(int type, String text) {
@@ -544,7 +561,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
         }
     }
 
-    protected boolean createAndAddShortcut(ShortcutInfo item) {
+    protected View createAndAddShortcut(ShortcutInfo item) {
         final BubbleTextView textView =
             (BubbleTextView) mInflater.inflate(R.layout.application, this, false);
         textView.setCompoundDrawables(null,
@@ -565,7 +582,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
             // This shouldn't happen, log it. 
             Log.e(TAG, "Folder order not properly persisted during bind");
             if (!findAndSetEmptyCells(item)) {
-                return false;
+                return null;
             }
         }
 
@@ -574,7 +591,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
         boolean insert = false;
         textView.setOnKeyListener(new FolderKeyEventListener());
         mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true);
-        return true;
+        return textView;
     }
 
     public void onDragEnter(DragObject d) {
@@ -723,6 +740,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
         mCurrentDragView = null;
         mSuppressOnAdd = false;
         mRearrangeOnClose = true;
+        mIsExternalDrag = false;
     }
 
     public void onDragExit(DragObject d) {
@@ -756,7 +774,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
                 success && (!beingCalledAfterUninstall || mUninstallSuccessful);
 
         if (successfulDrop) {
-            if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon) {
+            if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
                 replaceFolderWithFinalItem();
             }
         } else {
@@ -1152,34 +1170,75 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
     }
 
     public void onDrop(DragObject d) {
-        ShortcutInfo item;
-        if (d.dragInfo instanceof AppInfo) {
-            // Came from all apps -- make a copy
-            item = ((AppInfo) d.dragInfo).makeShortcut();
-            item.spanX = 1;
-            item.spanY = 1;
-        } else {
-            item = (ShortcutInfo) d.dragInfo;
+        Runnable cleanUpRunnable = null;
+
+        // If we are coming from All Apps space, we need to remove the extra empty screen (which is
+        // normally done in Workspace#onDropExternal, as well zoom back in and close the folder.
+        if (d.dragSource != mLauncher.getWorkspace() && !(d.dragSource instanceof Folder)) {
+            cleanUpRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    mLauncher.getWorkspace().removeExtraEmptyScreen(false, new Runnable() {
+                        @Override
+                        public void run() {
+                            mLauncher.closeFolder();
+                            mLauncher.exitSpringLoadedDragModeDelayed(true,
+                                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE,
+                                    null);
+                        }
+                    }, CLOSE_FOLDER_DELAY_MS, false);
+                }
+            };
         }
-        // Dragged from self onto self, currently this is the only path possible, however
-        // we keep this as a distinct code path.
-        if (item == mCurrentDragInfo) {
-            ShortcutInfo si = (ShortcutInfo) mCurrentDragView.getTag();
-            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mCurrentDragView.getLayoutParams();
+
+        View currentDragView;
+        ShortcutInfo si = mCurrentDragInfo;
+        if (mIsExternalDrag) {
+            si.cellX = mEmptyCell[0];
+            si.cellY = mEmptyCell[1];
+            currentDragView = createAndAddShortcut(si);
+        } else {
+            currentDragView = mCurrentDragView;
+            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) currentDragView.getLayoutParams();
             si.cellX = lp.cellX = mEmptyCell[0];
             si.cellX = lp.cellY = mEmptyCell[1];
-            mContent.addViewToCellLayout(mCurrentDragView, -1, (int)item.id, lp, true);
-            if (d.dragView.hasDrawn()) {
-                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, mCurrentDragView);
-            } else {
-                d.deferDragViewCleanupPostAnimation = false;
-                mCurrentDragView.setVisibility(VISIBLE);
+            mContent.addViewToCellLayout(currentDragView, -1, (int) si.id, lp, true);
+        }
+
+        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;
+        setupContentDimensions(getItemCount());
+
+        // Actually move the item in the database if it was an external drag.
+        if (mIsExternalDrag) {
+            LauncherModel.addOrMoveItemInDatabase(
+                    mLauncher, 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();
             }
-            mItemsInvalidated = true;
-            setupContentDimensions(getItemCount());
-            mSuppressOnAdd = true;
+            mIsExternalDrag = false;
         }
-        mInfo.add(item);
+
+        // Temporarily suppress the listener, as we did all the work already here.
+        mSuppressOnAdd = true;
+        mInfo.add(si);
+        mSuppressOnAdd = false;
     }
 
     // This is used so the item doesn't immediately appear in the folder when added. In one case
index 644db47..78026f1 100644 (file)
@@ -75,10 +75,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
     // Flag as to whether or not to draw an outer ring. Currently none is designed.
     public static final boolean HAS_OUTER_RING = true;
 
+    // Flag whether the folder should open itself when an item is dragged over is enabled.
+    public static final boolean SPRING_LOADING_ENABLED = true;
+
     // The degree to which the item in the back of the stack is scaled [0...1]
     // (0 means it's not scaled at all, 1 means it's scaled to nothing)
     private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
 
+    // Delay when drag enters until the folder opens, in miliseconds.
+    private static final int ON_OPEN_DELAY = 800;
+
     public static Drawable sSharedFolderLeaveBehind = null;
 
     private ImageView mPreviewBackground;
@@ -103,6 +109,9 @@ public class FolderIcon extends FrameLayout implements FolderListener {
     private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
     private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
 
+    private Alarm mOpenAlarm = new Alarm();
+    private ItemInfo mDragInfo;
+
     public FolderIcon(Context context, AttributeSet attrs) {
         super(context, attrs);
         init();
@@ -331,11 +340,32 @@ public class FolderIcon extends FrameLayout implements FolderListener {
         mFolderRingAnimator.setCellLayout(layout);
         mFolderRingAnimator.animateToAcceptState();
         layout.showFolderAccept(mFolderRingAnimator);
+        mOpenAlarm.setOnAlarmListener(mOnOpenListener);
+        if (SPRING_LOADING_ENABLED) {
+            mOpenAlarm.setAlarm(ON_OPEN_DELAY);
+        }
+        mDragInfo = (ItemInfo) dragInfo;
     }
 
     public void onDragOver(Object dragInfo) {
     }
 
+    OnAlarmListener mOnOpenListener = new OnAlarmListener() {
+        public void onAlarm(Alarm alarm) {
+            ShortcutInfo item;
+            if (mDragInfo instanceof AppInfo) {
+                // Came from all apps -- make a copy.
+                item = ((AppInfo) mDragInfo).makeShortcut();
+                item.spanX = 1;
+                item.spanY = 1;
+            } else {
+                item = (ShortcutInfo) mDragInfo;
+            }
+            mFolder.beginExternalDrag(item);
+            mLauncher.openFolder(FolderIcon.this);
+        }
+    };
+
     public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
             final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
             float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
@@ -371,6 +401,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
 
     public void onDragExit() {
         mFolderRingAnimator.animateToNaturalState();
+        mOpenAlarm.cancelAlarm();
     }
 
     private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
index c57d32e..c87f9e2 100644 (file)
@@ -210,6 +210,7 @@ public class Launcher extends Activity
 
     static final int APPWIDGET_HOST_ID = 1024;
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
+    public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE = 400;
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
 
     private static final Object sLock = new Object();