Merge "Trim all whitespace from titles and labels." into ub-launcher3-burnaby
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index cd3a051..5bacc96 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -19,7 +19,7 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="@drawable/quantum_panel"
+    android:elevation="5dp"
     android:orientation="vertical" >
 
     <FrameLayout
diff --git a/res/values/config.xml b/res/values/config.xml
index 6ef8635..84ccef1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -102,4 +102,5 @@
     <item type="id" name="action_move_to_workspace" />
     <item type="id" name="action_move_screen_backwards" />
     <item type="id" name="action_move_screen_forwards" />
+    <item type="id" name="action_resize" />
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 46830d6..4d33b88 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -76,10 +76,14 @@
          or right while you're dragging. -->
     <dimen name="scroll_zone">20dp</dimen>
 
-    <!--  When dragging an item, how much bigger (fixed dps) the dragged view
-          should be. If 0, it will not be scaled at all. -->
+    <!-- When dragging an item, how much bigger (fixed dps) the dragged view
+         should be. If 0, it will not be scaled at all. -->
     <dimen name="dragViewScale">12dp</dimen>
 
+    <!-- Elevation for the drag view. It should be larger than elevation of all other drag sources
+         and drop targets like all-apps and folders -->
+    <dimen name="drag_elevation">30dp</dimen>
+
 <!-- Widget tray -->
     <dimen name="widget_container_inset">8dp</dimen>
     <dimen name="widget_preview_size">120dp</dimen>
@@ -108,6 +112,7 @@
     <!-- The amount that the preview contents are inset from the preview background -->
     <dimen name="folder_preview_padding">4dp</dimen>
     <dimen name="folder_name_padding">10dp</dimen>
+    <dimen name="folder_shadow_padding">8dp</dimen>
 
 <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1681fc6..5962584 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -252,4 +252,23 @@
 
     <!-- Accessibility confirmation when a screen was moved [DO NOT TRANSLATE] -->
     <string name="screen_moved">Screen moved</string>
+
+    <!-- Accessibility action to resize a widget [DO NOT TRANSLATE] -->
+    <string name="action_resize">Resize</string>
+
+    <!-- Accessibility action to increase width of a widget [DO NOT TRANSLATE] -->
+    <string name="action_increase_width">Increase width</string>
+
+    <!-- Accessibility action to increase height of a widget [DO NOT TRANSLATE] -->
+    <string name="action_increase_height">Increase height</string>
+
+    <!-- Accessibility action to decrease width of a widget [DO NOT TRANSLATE] -->
+    <string name="action_decrease_width">Decrease width</string>
+
+    <!-- Accessibility action to decrease height of a widget [DO NOT TRANSLATE] -->
+    <string name="action_decrease_height">Decrease height</string>
+
+    <!-- Accessibility confirmation for widget resize [DO NOT TRANSLATE]-->
+    <string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string>
+
 </resources>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 65c6702..72eabf1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -3051,4 +3051,21 @@
     public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
         return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
     }
+
+    public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
+        int x2 = x + spanX - 1;
+        int y2 = y + spanY - 1;
+        if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
+            return false;
+        }
+        for (int i = x; i <= x2; i++) {
+            for (int j = y; j <= y2; j++) {
+                if (mOccupied[i][j]) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 91f97fa..2efdb06 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -38,7 +38,6 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -153,6 +152,14 @@
         return false;
     }
 
+    private boolean isEventOverDropTargetBar(MotionEvent ev) {
+        getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect);
+        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+            return true;
+        }
+        return false;
+    }
+
     public void setBlockTouch(boolean block) {
         mBlockTouches = block;
     }
@@ -188,10 +195,16 @@
                 }
             }
 
-            getDescendantRectRelativeToSelf(currentFolder, hitRect);
             if (!isEventOverFolder(currentFolder, ev)) {
-                mLauncher.closeFolder();
-                return true;
+                if (isInAccessibleDrag()) {
+                    // Do not close the folder if in drag and drop.
+                    if (!isEventOverDropTargetBar(ev)) {
+                        return true;
+                    }
+                } else {
+                    mLauncher.closeFolder();
+                    return true;
+                }
             }
         }
         return false;
@@ -228,11 +241,12 @@
                         getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
             if (accessibilityManager.isTouchExplorationEnabled()) {
                 final int action = ev.getAction();
-                boolean isOverFolder;
+                boolean isOverFolderOrSearchBar;
                 switch (action) {
                     case MotionEvent.ACTION_HOVER_ENTER:
-                        isOverFolder = isEventOverFolder(currentFolder, ev);
-                        if (!isOverFolder) {
+                        isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
+                            (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+                        if (!isOverFolderOrSearchBar) {
                             sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
                             mHoverPointClosesFolder = true;
                             return true;
@@ -240,12 +254,13 @@
                         mHoverPointClosesFolder = false;
                         break;
                     case MotionEvent.ACTION_HOVER_MOVE:
-                        isOverFolder = isEventOverFolder(currentFolder, ev);
-                        if (!isOverFolder && !mHoverPointClosesFolder) {
+                        isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
+                            (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+                        if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
                             sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
                             mHoverPointClosesFolder = true;
                             return true;
-                        } else if (!isOverFolder) {
+                        } else if (!isOverFolderOrSearchBar) {
                             return true;
                         }
                         mHoverPointClosesFolder = false;
@@ -268,6 +283,12 @@
         }
     }
 
+    private boolean isInAccessibleDrag() {
+        LauncherAccessibilityDelegate delegate = LauncherAppState
+                .getInstance().getAccessibilityDelegate();
+        return delegate != null && delegate.isInAccessibleDrag();
+    }
+
     @Override
     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
         Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
@@ -275,6 +296,10 @@
             if (child == currentFolder) {
                 return super.onRequestSendAccessibilityEvent(child, event);
             }
+
+            if (isInAccessibleDrag() && child instanceof SearchDropTargetBar) {
+                return super.onRequestSendAccessibilityEvent(child, event);
+            }
             // Skip propagating onRequestSendAccessibilityEvent all for other children
             // when a folder is open
             return false;
@@ -288,6 +313,10 @@
         if (currentFolder != null) {
             // Only add the folder as a child for accessibility when it is open
             childrenForAccessibility.add(currentFolder);
+
+            if (isInAccessibleDrag()) {
+                childrenForAccessibility.add(mLauncher.getSearchBar());
+            }
         } else {
             super.addChildrenForAccessibility(childrenForAccessibility);
         }
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index a4b6704..3eec3d9 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -129,6 +129,10 @@
         int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
         measure(ms, ms);
         mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+
+        if (Utilities.isLmpOrAbove()) {
+            setElevation(getResources().getDimension(R.dimen.drag_elevation));
+        }
     }
 
     /** Sets the scale of the view over the normal workspace icon size. */
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 7a3ae39..6ca4888 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -27,6 +27,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.text.InputType;
 import android.text.Selection;
@@ -180,6 +181,15 @@
         // name is complete, we have something to focus on, thus hiding the cursor and giving
         // reliable behavior when clicking the text field (since it will always gain focus on click).
         setFocusableInTouchMode(true);
+
+        if (Utilities.isLmpOrAbove()) {
+            int padding = getResources().getDimensionPixelSize(R.dimen.folder_shadow_padding);
+            setBackground(new InsetDrawable(
+                    getResources().getDrawable(R.drawable.apps_list_bg),
+                    padding, padding, padding, padding));
+        } else {
+            setBackgroundResource(R.drawable.quantum_panel);
+        }
     }
 
     @Override
@@ -275,6 +285,9 @@
         for (int i = 0; i < mContent.getChildCount(); i++) {
             mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
         }
+
+        mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
+            IMPORTANT_FOR_ACCESSIBILITY_AUTO);
         mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
     }
 
@@ -471,9 +484,15 @@
             PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0);
             PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0);
 
+            Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty);
+            drift.setDuration(mMaterialExpandDuration);
+            drift.setStartDelay(mMaterialExpandStagger);
+            drift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
             int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
             int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
             float radius = (float) Math.hypot(rx, ry);
+
             AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
             Animator reveal = LauncherAnimUtils.createCircularReveal(this, (int) getPivotX(),
                     (int) getPivotY(), 0, radius);
@@ -492,10 +511,6 @@
             textAlpha.setStartDelay(mMaterialExpandStagger);
             textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
 
-            Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty);
-            drift.setDuration(mMaterialExpandDuration);
-            drift.setStartDelay(mMaterialExpandStagger);
-            drift.setInterpolator(new LogDecelerateInterpolator(60, 0));
 
             anim.play(drift);
             anim.play(iconsAlpha);
@@ -505,10 +520,12 @@
             openFolderAnim = anim;
 
             mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null);
+            mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
             onCompleteRunnable = new Runnable() {
                 @Override
                 public void run() {
                     mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
+                    mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
                 }
             };
         }
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index a1c909a..211bbfe 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -504,6 +504,10 @@
         }
     }
 
+    public int getAllocatedContentSize() {
+        return mAllocatedContentSize;
+    }
+
     /**
      * Reorders the items such that the {@param empty} spot moves to {@param target}
      */
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index 8a9a050..3992e63 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -1,6 +1,9 @@
 package com.android.launcher3;
 
 import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.DialogInterface;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
@@ -28,6 +31,7 @@
     private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
     private static final int MOVE = R.id.action_move;
     private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
+    private static final int RESIZE = R.id.action_resize;
 
     public enum DragType {
         ICON,
@@ -62,6 +66,8 @@
                 launcher.getText(R.string.action_move)));
         mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE,
                 launcher.getText(R.string.action_move_to_workspace)));
+        mActions.put(RESIZE, new AccessibilityAction(RESIZE,
+                        launcher.getText(R.string.action_resize)));
     }
 
     @Override
@@ -87,6 +93,10 @@
 
             if (item.container >= 0) {
                 info.addAction(mActions.get(MOVE_TO_WORKSPACE));
+            } else if (item instanceof LauncherAppWidgetInfo) {
+                if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) {
+                    info.addAction(mActions.get(RESIZE));
+                }
             }
         } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
             info.addAction(mActions.get(ADD_TO_WORKSPACE));
@@ -102,7 +112,7 @@
         return super.performAccessibilityAction(host, action, args);
     }
 
-    public boolean performAction(View host, final ItemInfo item, int action) {
+    public boolean performAction(final View host, final ItemInfo item, int action) {
         if (action == REMOVE) {
             if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) {
                 announceConfirmation(R.string.item_removed);
@@ -167,10 +177,97 @@
                     announceConfirmation(R.string.item_moved);
                 }
             });
+        } else if (action == RESIZE) {
+            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
+            final ArrayList<Integer> actions = getSupportedResizeActions(host, info);
+            CharSequence[] labels = new CharSequence[actions.size()];
+            for (int i = 0; i < actions.size(); i++) {
+                labels[i] = mLauncher.getText(actions.get(i));
+            }
+
+            new AlertDialog.Builder(mLauncher)
+                .setTitle(R.string.action_resize)
+                .setItems(labels, new DialogInterface.OnClickListener() {
+
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        performResizeAction(actions.get(which), host, info);
+                        dialog.dismiss();
+                    }
+                })
+                .show();
         }
         return false;
     }
 
+    private ArrayList<Integer> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
+        AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
+        ArrayList<Integer> actions = new ArrayList<>();
+
+        CellLayout layout = (CellLayout) host.getParent().getParent();
+        if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
+            if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
+                    layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
+                actions.add(R.string.action_increase_width);
+            }
+
+            if (info.spanX > info.minSpanX && info.spanX > 1) {
+                actions.add(R.string.action_decrease_width);
+            }
+        }
+
+        if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
+            if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
+                    layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
+                actions.add(R.string.action_increase_height);
+            }
+
+            if (info.spanY > info.minSpanY && info.spanY > 1) {
+                actions.add(R.string.action_decrease_height);
+            }
+        }
+        return actions;
+    }
+
+    private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
+        CellLayout layout = (CellLayout) host.getParent().getParent();
+        layout.markCellsAsUnoccupiedForView(host);
+
+        if (action == R.string.action_increase_width) {
+            if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
+                    && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
+                    || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
+                lp.cellX --;
+                info.cellX --;
+            }
+            lp.cellHSpan ++;
+            info.spanX ++;
+        } else if (action == R.string.action_decrease_width) {
+            lp.cellHSpan --;
+            info.spanX --;
+        } else if (action == R.string.action_increase_height) {
+            if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
+                lp.cellY --;
+                info.cellY --;
+            }
+            lp.cellVSpan ++;
+            info.spanY ++;
+        } else if (action == R.string.action_decrease_height) {
+            lp.cellVSpan --;
+            info.spanY --;
+        }
+
+        layout.markCellsAsOccupiedForView(host);
+        Rect sizeRange = new Rect();
+        AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange);
+        ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
+                sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
+        host.requestLayout();
+        LauncherModel.updateItemInDatabase(mLauncher, info);
+        announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
+    }
+
     @Thunk void announceConfirmation(int resId) {
         announceConfirmation(mLauncher.getResources().getString(resId));
     }
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
index fc105b4..ff99890 100644
--- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -24,23 +24,29 @@
  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder.
  */
 public class FolderAccessibilityHelper extends DragAndDropAccessibilityDelegate {
+
+    /**
+     * 0-index position for the first cell in {@link #mView} in {@link #mParent}.
+     */
     private final int mStartPosition;
 
+    private final FolderPagedView mParent;
+
     public FolderAccessibilityHelper(CellLayout layout) {
         super(layout);
-        FolderPagedView parent = (FolderPagedView) layout.getParent();
+        mParent = (FolderPagedView) layout.getParent();
 
-        int index = parent.indexOfChild(layout);
-        mStartPosition = 1 + index * layout.getCountX() * layout.getCountY();
+        int index = mParent.indexOfChild(layout);
+        mStartPosition = index * layout.getCountX() * layout.getCountY();
     }
     @Override
     protected int intersectsValidDropTarget(int id) {
-        return id;
+        return Math.min(id, mParent.getAllocatedContentSize() - mStartPosition - 1);
     }
 
     @Override
     protected String getLocationDescriptionForIconDrop(int id) {
-        return mContext.getString(R.string.move_to_position, id + mStartPosition);
+        return mContext.getString(R.string.move_to_position, id + mStartPosition + 1);
     }
 
     @Override