diff options
Diffstat (limited to 'src/com/android')
78 files changed, 2321 insertions, 1691 deletions
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java new file mode 100644 index 000000000..eaac6be80 --- /dev/null +++ b/src/com/android/launcher3/AnotherWindowDropTarget.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 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; + +import android.content.Context; +import android.graphics.PointF; +import android.graphics.Rect; + +/** + * Drop target used when another window (i.e. another process) has accepted a global system drag. + * If the accepted item was a shortcut, we delete it from Launcher. + */ +public class AnotherWindowDropTarget implements DropTarget { + final Launcher mLauncher; + + public AnotherWindowDropTarget (Context context) { mLauncher = (Launcher) context; } + + @Override + public boolean isDropEnabled() { return true; } + + @Override + public void onDrop(DragObject dragObject) { + dragObject.deferDragViewCleanupPostAnimation = false; + LauncherModel.deleteItemFromDatabase(mLauncher, (ShortcutInfo) dragObject.dragInfo); + } + + @Override + public void onDragEnter(DragObject dragObject) {} + + @Override + public void onDragOver(DragObject dragObject) {} + + @Override + public void onDragExit(DragObject dragObject) {} + + @Override + public void onFlingToDelete(DragObject dragObject, PointF vec) {} + + @Override + public boolean acceptDrop(DragObject dragObject) { + return dragObject.dragInfo instanceof ShortcutInfo; + } + + @Override + public void prepareAccessibilityDrop() {} + + // These methods are implemented in Views + @Override + public void getHitRectRelativeToDragLayer(Rect outRect) {} + + @Override + public void getLocationInDragLayer(int[] loc) {} + + @Override + public int getLeft() { return 0; } + + @Override + public int getTop() { return 0; } +} diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index c95d5585a..7249c6406 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -28,13 +28,11 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import java.util.ArrayList; -import java.util.Arrays; /** * Represents an app in AllAppsView. */ public class AppInfo extends ItemInfo { - private static final String TAG = "Launcher3.AppInfo"; /** * The intent used to start the application. @@ -118,8 +116,7 @@ public class AppInfo extends ItemInfo { return "ApplicationInfo(title=" + title + " id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY - + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) - + " user=" + user + ")"; + + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")"; } /** @@ -128,7 +125,7 @@ public class AppInfo extends ItemInfo { public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) { Log.d(tag, label + " size=" + list.size()); for (AppInfo info: list) { - Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap + Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap + " firstInstallTime=" + info.firstInstallTime + " componentName=" + info.componentName.getPackageName()); } diff --git a/src/com/android/launcher3/AppInfoDropTargetBar.java b/src/com/android/launcher3/AppInfoDropTargetBar.java new file mode 100644 index 000000000..31ff42a7f --- /dev/null +++ b/src/com/android/launcher3/AppInfoDropTargetBar.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 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; + +import android.content.Context; +import android.util.AttributeSet; + +import com.android.launcher3.dragndrop.DragController; + +public class AppInfoDropTargetBar extends BaseDropTargetBar { + private ButtonDropTarget mAppInfoDropTarget; + + public AppInfoDropTargetBar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AppInfoDropTargetBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // Get the individual components + mAppInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text); + + mAppInfoDropTarget.setDropTargetBar(this); + } + + @Override + public void setup(Launcher launcher, DragController dragController) { + dragController.addDragListener(this); + + dragController.addDragListener(mAppInfoDropTarget); + + dragController.addDropTarget(mAppInfoDropTarget); + + mAppInfoDropTarget.setLauncher(launcher); + } + + @Override + public void showDropTarget() { + animateDropTargetBarToAlpha(1f, DEFAULT_DRAG_FADE_DURATION); + } + + @Override + public void hideDropTarget() { + animateDropTargetBarToAlpha(0f, DEFAULT_DRAG_FADE_DURATION); + } + + private void animateDropTargetBarToAlpha(float alpha, int duration) { + animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, alpha,duration); + } + + @Override + public void enableAccessibleDrag(boolean enable) { + mAppInfoDropTarget.enableAccessibleDrag(enable); + } +} diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index d86853608..7bd5284fc 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -1,5 +1,7 @@ package com.android.launcher3; +import com.android.launcher3.dragndrop.DragLayer; + import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -9,6 +11,7 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.view.Gravity; import android.widget.FrameLayout; @@ -21,7 +24,10 @@ public class AppWidgetResizeFrame extends FrameLayout { private static final float DIMMED_HANDLE_ALPHA = 0f; private static final float RESIZE_THRESHOLD = 0.66f; - private static Rect sTmpRect = new Rect(); + private static final Rect sTmpRect = new Rect(); + + // Represents the cell size on the grid in the two orientations. + private static Point[] sCellSize; private final Launcher mLauncher; private final LauncherAppWidgetHostView mWidgetView; @@ -353,28 +359,27 @@ public class AppWidgetResizeFrame extends FrameLayout { } public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) { + if (sCellSize == null) { + InvariantDeviceProfile inv = LauncherAppState.getInstance().getInvariantDeviceProfile(); + + // Initiate cell sizes. + sCellSize = new Point[2]; + sCellSize[0] = inv.landscapeProfile.getCellSize(); + sCellSize[1] = inv.portraitProfile.getCellSize(); + } + if (rect == null) { rect = new Rect(); } - Rect landMetrics = Workspace.getCellLayoutMetrics(launcher, CellLayout.LANDSCAPE); - Rect portMetrics = Workspace.getCellLayoutMetrics(launcher, CellLayout.PORTRAIT); final float density = launcher.getResources().getDisplayMetrics().density; // Compute landscape size - int cellWidth = landMetrics.left; - int cellHeight = landMetrics.top; - int widthGap = landMetrics.right; - int heightGap = landMetrics.bottom; - int landWidth = (int) ((spanX * cellWidth + (spanX - 1) * widthGap) / density); - int landHeight = (int) ((spanY * cellHeight + (spanY - 1) * heightGap) / density); + int landWidth = (int) ((spanX * sCellSize[0].x) / density); + int landHeight = (int) ((spanY * sCellSize[0].y) / density); // Compute portrait size - cellWidth = portMetrics.left; - cellHeight = portMetrics.top; - widthGap = portMetrics.right; - heightGap = portMetrics.bottom; - int portWidth = (int) ((spanX * cellWidth + (spanX - 1) * widthGap) / density); - int portHeight = (int) ((spanY * cellHeight + (spanY - 1) * heightGap) / density); + int portWidth = (int) ((spanX * sCellSize[1].x) / density); + int portHeight = (int) ((spanY * sCellSize[1].y) / density); rect.set(portWidth, landHeight, landWidth, portHeight); return rect; } @@ -453,10 +458,10 @@ public class AppWidgetResizeFrame extends FrameLayout { PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY); ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y); - ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, "alpha", 1.0f); - ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, "alpha", 1.0f); - ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, "alpha", 1.0f); - ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, "alpha", 1.0f); + ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, ALPHA, 1.0f); + ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, ALPHA, 1.0f); + ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, ALPHA, 1.0f); + ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, ALPHA, 1.0f); oa.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { requestLayout(); diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java index c11824054..e0946ea14 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -22,6 +22,8 @@ import android.util.AttributeSet; import android.util.Log; import android.widget.LinearLayout; +import com.android.launcher3.config.ProviderConfig; + /** * A base container view, which supports resizing. */ @@ -71,7 +73,7 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab * Sets the search bar bounds for this container view to match. */ final public void setSearchBarBounds(Rect bounds) { - if (LauncherAppState.isDogfoodBuild() && !isValidSearchBarBounds(bounds)) { + if (ProviderConfig.IS_DOGFOOD_BUILD && !isValidSearchBarBounds(bounds)) { Log.e(TAG, "Invalid search bar bounds: " + bounds); } diff --git a/src/com/android/launcher3/BaseDropTargetBar.java b/src/com/android/launcher3/BaseDropTargetBar.java new file mode 100644 index 000000000..f478a3530 --- /dev/null +++ b/src/com/android/launcher3/BaseDropTargetBar.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 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; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.widget.FrameLayout; + +import com.android.launcher3.dragndrop.DragController; + +/** + * Base class for drop target bars (where you can drop apps to do actions such as uninstall). + */ +public abstract class BaseDropTargetBar extends FrameLayout implements DragController.DragListener { + protected static final int DEFAULT_DRAG_FADE_DURATION = 175; + + protected View mDropTargetBar; + + protected LauncherViewPropertyAnimator mDropTargetBarAnimator; + protected static final AccelerateInterpolator sAccelerateInterpolator = + new AccelerateInterpolator(); + protected boolean mAccessibilityEnabled = false; + + protected boolean mDeferOnDragEnd; + + public BaseDropTargetBar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BaseDropTargetBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mDropTargetBar = findViewById(R.id.drag_target_bar); + + // Create the various fade animations + mDropTargetBar.setAlpha(0f); + mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar); + mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator); + mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // Ensure that the view is visible for the animation + mDropTargetBar.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mDropTargetBar != null) { + AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled); + } + } + }); + } + + + /** + * Convenience method to animate the alpha of a view using hardware layers. + */ + protected void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha, + int duration) { + if (v == null) { + return; + } + + animator.cancel(); + if (Float.compare(v.getAlpha(), alpha) != 0) { + if (duration > 0) { + animator.alpha(alpha).withLayer().setDuration(duration).start(); + } else { + v.setAlpha(alpha); + AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled); + } + } + } + + /* + * DragController.DragListener implementation + */ + @Override + public void onDragStart(DragSource source, ItemInfo info, int dragAction) { + showDropTarget(); + } + + /** + * This is called to defer hiding the delete drop target until the drop animation has completed, + * instead of hiding immediately when the drag has ended. + */ + protected void deferOnDragEnd() { + mDeferOnDragEnd = true; + } + + @Override + public void onDragEnd() { + if (!mDeferOnDragEnd) { + hideDropTarget(); + } else { + mDeferOnDragEnd = false; + } + } + + public abstract void showDropTarget(); + + public abstract void hideDropTarget(); + + public abstract void enableAccessibleDrag(boolean enable); + + public abstract void setup(Launcher launcher, DragController dragController); +} diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 507087824..c8af600ba 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -144,7 +144,7 @@ public class BubbleTextView extends TextView } mLongPressHelper = new CheckLongPressHelper(this); - mStylusEventHelper = new StylusEventHelper(this); + mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); if (mCustomShadowsEnabled) { @@ -270,7 +270,7 @@ public class BubbleTextView extends TextView boolean result = super.onTouchEvent(event); // Check for a stylus button press, if it occurs cancel any long press checks. - if (mStylusEventHelper.checkAndPerformStylusEvent(event)) { + if (mStylusEventHelper.onMotionEvent(event)) { mLongPressHelper.cancelLongPress(); result = true; } diff --git a/src/com/android/launcher3/BuildInfo.java b/src/com/android/launcher3/BuildInfo.java index b49ee0d9b..1392d7a43 100644 --- a/src/com/android/launcher3/BuildInfo.java +++ b/src/com/android/launcher3/BuildInfo.java @@ -1,32 +1,9 @@ package com.android.launcher3; -import android.text.TextUtils; -import android.util.Log; - +// TODO: Remove this class once all its references are gone. public class BuildInfo { - private static final boolean DBG = false; - private static final String TAG = "BuildInfo"; public boolean isDogfoodBuild() { return false; } - - public static BuildInfo loadByName(String className) { - if (TextUtils.isEmpty(className)) return new BuildInfo(); - - if (DBG) Log.d(TAG, "Loading BuildInfo: " + className); - try { - Class<?> cls = Class.forName(className); - return (BuildInfo) cls.newInstance(); - } catch (ClassNotFoundException e) { - Log.e(TAG, "Bad BuildInfo class", e); - } catch (InstantiationException e) { - Log.e(TAG, "Bad BuildInfo class", e); - } catch (IllegalAccessException e) { - Log.e(TAG, "Bad BuildInfo class", e); - } catch (ClassCastException e) { - Log.e(TAG, "Bad BuildInfo class", e); - } - return new BuildInfo(); - } } diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 9caa7ad87..56f209e8c 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -39,6 +39,9 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import android.widget.TextView; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.util.Thunk; /** @@ -47,11 +50,11 @@ import com.android.launcher3.util.Thunk; public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener, OnClickListener { - private static int DRAG_VIEW_DROP_DURATION = 285; + private static final int DRAG_VIEW_DROP_DURATION = 285; protected Launcher mLauncher; private int mBottomDragPadding; - protected SearchDropTargetBar mSearchDropTargetBar; + protected BaseDropTargetBar mDropTargetBar; /** Whether this drop target is active for the current drag */ protected boolean mActive; @@ -104,8 +107,8 @@ public abstract class ButtonDropTarget extends TextView mLauncher = launcher; } - public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) { - mSearchDropTargetBar = searchDropTargetBar; + public void setDropTargetBar(BaseDropTargetBar dropTargetBar) { + mDropTargetBar = dropTargetBar; } @Override @@ -186,8 +189,8 @@ public abstract class ButtonDropTarget extends TextView } } - @Override - public final void onDragStart(DragSource source, Object info, int dragAction) { + @Override + public final void onDragStart(DragSource source, ItemInfo info, int dragAction) { mActive = supportsDrop(source, info); mDrawable.setColorFilter(null); if (mCurrentColorAnim != null) { @@ -203,7 +206,7 @@ public abstract class ButtonDropTarget extends TextView return supportsDrop(dragObject.dragSource, dragObject.dragInfo); } - protected abstract boolean supportsDrop(DragSource source, Object info); + protected abstract boolean supportsDrop(DragSource source, ItemInfo info); @Override public boolean isDropEnabled() { @@ -229,13 +232,13 @@ public abstract class ButtonDropTarget extends TextView final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), width, height); final float scale = (float) to.width() / from.width(); - mSearchDropTargetBar.deferOnDragEnd(); + mDropTargetBar.deferOnDragEnd(); Runnable onAnimationEndRunnable = new Runnable() { @Override public void run() { completeDrop(d); - mSearchDropTargetBar.onDragEnd(); + mDropTargetBar.onDragEnd(); mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null); } }; diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 5ae7310ff..92e4043df 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -18,14 +18,12 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; -import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -52,9 +50,12 @@ import android.widget.Toast; import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler; import com.android.launcher3.FolderIcon.FolderRingAnimator; +import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.util.ParcelableSparseArray; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -86,6 +87,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private int mMaxGap; private boolean mDropPending = false; private boolean mIsDragTarget = true; + private boolean mJailContent = true; // These are temporary variables to prevent having to allocate a new object just to // return an (x, y) value from helper functions. Do NOT use them to maintain other state. @@ -189,7 +191,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mLauncher = (Launcher) context; DeviceProfile grid = mLauncher.getDeviceProfile(); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0); mCellWidth = mCellHeight = -1; mFixedCellWidth = mFixedCellHeight = -1; @@ -203,10 +204,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mPreviousReorderDirection[0] = INVALID_DIRECTION; mPreviousReorderDirection[1] = INVALID_DIRECTION; - a.recycle(); - setAlwaysDrawnWithCacheEnabled(false); - final Resources res = getResources(); mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx; @@ -278,7 +276,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap, mCountX, mCountY); - mStylusEventHelper = new StylusEventHelper(this); + mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); mTouchFeedbackView = new ClickShadowView(context); addView(mTouchFeedbackView); @@ -340,7 +338,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // enabled to allow rearranging the different home screens. So check what mode // the workspace is in, and only perform stylus button presses while in overview mode. if (mLauncher.mWorkspace.isInOverviewMode() - && mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + && mStylusEventHelper.onMotionEvent(ev)) { return true; } return handled; @@ -406,7 +404,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mIsDragTarget = false; } - boolean isDragTarget() { + public boolean isDragTarget() { return mIsDragTarget; } @@ -426,7 +424,33 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - boolean getIsDragOverlapping() { + public void disableJailContent() { + mJailContent = false; + } + + @Override + protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { + if (mJailContent) { + ParcelableSparseArray jail = getJailedArray(container); + super.dispatchSaveInstanceState(jail); + container.put(R.id.cell_layout_jail_id, jail); + } else { + super.dispatchSaveInstanceState(container); + } + } + + @Override + protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { + super.dispatchRestoreInstanceState(mJailContent ? getJailedArray(container) : container); + } + + private ParcelableSparseArray getJailedArray(SparseArray<Parcelable> container) { + final Parcelable parcelable = container.get(R.id.cell_layout_jail_id); + return parcelable instanceof ParcelableSparseArray ? + (ParcelableSparseArray) parcelable : new ParcelableSparseArray(); + } + + public boolean getIsDragOverlapping() { return mIsDragOverlapping; } @@ -449,12 +473,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { for (int i = 0; i < mDragOutlines.length; i++) { final float alpha = mDragOutlineAlphas[i]; if (alpha > 0) { - final Rect r = mDragOutlines[i]; - mTempRect.set(r); - Utilities.scaleRectAboutCenter(mTempRect, getChildrenScale()); final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag(); paint.setAlpha((int)(alpha + .5f)); - canvas.drawBitmap(b, null, mTempRect, paint); + canvas.drawBitmap(b, null, mDragOutlines[i], paint); } } @@ -569,7 +590,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { try { dispatchRestoreInstanceState(states); } catch (IllegalArgumentException ex) { - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { throw ex; } // Mismatched viewId / viewType preventing restore. Skip restore on production builds. @@ -1035,53 +1056,57 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mDragCell[0] = cellX; mDragCell[1] = cellY; - // Find the top left corner of the rect the object will occupy - final int[] topLeft = mTmpPoint; - cellToPoint(cellX, cellY, topLeft); - - int left = topLeft[0]; - int top = topLeft[1]; - - if (v != null && dragOffset == null) { - // When drawing the drag outline, it did not account for margin offsets - // added by the view's parent. - MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams(); - left += lp.leftMargin; - top += lp.topMargin; - - // Offsets due to the size difference between the View and the dragOutline. - // There is a size difference to account for the outer blur, which may lie - // outside the bounds of the view. - top += (v.getHeight() - dragOutline.getHeight()) / 2; - // We center about the x axis - left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap) - - dragOutline.getWidth()) / 2; - } else { - if (dragOffset != null && dragRegion != null) { - // Center the drag region *horizontally* in the cell and apply a drag - // outline offset - left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap) - - dragRegion.width()) / 2; - int cHeight = getShortcutsAndWidgets().getCellContentHeight(); - int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f)); - top += dragOffset.y + cellPaddingY; - } else { - // Center the drag outline in the cell - left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap) - - dragOutline.getWidth()) / 2; - top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap) - - dragOutline.getHeight()) / 2; - } - } + final int oldIndex = mDragOutlineCurrent; mDragOutlineAnims[oldIndex].animateOut(); mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length; Rect r = mDragOutlines[mDragOutlineCurrent]; - r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight()); + if (resize) { cellToRect(cellX, cellY, spanX, spanY, r); + } else { + // Find the top left corner of the rect the object will occupy + final int[] topLeft = mTmpPoint; + cellToPoint(cellX, cellY, topLeft); + + int left = topLeft[0]; + int top = topLeft[1]; + + if (v != null && dragOffset == null) { + // When drawing the drag outline, it did not account for margin offsets + // added by the view's parent. + MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams(); + left += lp.leftMargin; + top += lp.topMargin; + + // Offsets due to the size difference between the View and the dragOutline. + // There is a size difference to account for the outer blur, which may lie + // outside the bounds of the view. + top += (v.getHeight() - dragOutline.getHeight()) / 2; + // We center about the x axis + left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap) + - dragOutline.getWidth()) / 2; + } else { + if (dragOffset != null && dragRegion != null) { + // Center the drag region *horizontally* in the cell and apply a drag + // outline offset + left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap) + - dragRegion.width()) / 2; + int cHeight = getShortcutsAndWidgets().getCellContentHeight(); + int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f)); + top += dragOffset.y + cellPaddingY; + } else { + // Center the drag outline in the cell + left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap) + - dragOutline.getWidth()) / 2; + top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap) + - dragOutline.getHeight()) / 2; + } + } + r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight()); } + Utilities.scaleRectAboutCenter(r, getChildrenScale()); mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline); mDragOutlineAnims[mDragOutlineCurrent].animateIn(); @@ -1111,23 +1136,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { * * @param pixelX The X location at which you want to search for a vacant area. * @param pixelY The Y location at which you want to search for a vacant area. - * @param spanX Horizontal span of the object. - * @param spanY Vertical span of the object. - * @param result Array in which to place the result, or null (in which case a new array will - * be allocated) - * @return The X, Y cell of a vacant area that can contain this object, - * nearest the requested location. - */ - int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) { - return findNearestVacantArea(pixelX, pixelY, spanX, spanY, spanX, spanY, result, null); - } - - /** - * Find a vacant area that will fit the given bounds nearest the requested - * cell location. Uses Euclidean distance to score multiple vacant areas. - * - * @param pixelX The X location at which you want to search for a vacant area. - * @param pixelY The Y location at which you want to search for a vacant area. * @param minSpanX The minimum horizontal span required * @param minSpanY The minimum vertical span required * @param spanX Horizontal span of the object. @@ -2202,17 +2210,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { a.cancel(); } - AnimatorSet s = LauncherAnimUtils.createAnimatorSet(); - a = s; - s.playTogether( - LauncherAnimUtils.ofFloat(child, "scaleX", getChildrenScale()), - LauncherAnimUtils.ofFloat(child, "scaleY", getChildrenScale()), - LauncherAnimUtils.ofFloat(child, "translationX", 0f), - LauncherAnimUtils.ofFloat(child, "translationY", 0f) - ); - s.setDuration(REORDER_ANIMATION_DURATION); - s.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f)); - s.start(); + a = new LauncherViewPropertyAnimator(child) + .scaleX(getChildrenScale()) + .scaleY(getChildrenScale()) + .translationX(0) + .translationY(0) + .setDuration(REORDER_ANIMATION_DURATION); + a.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f)); + a.start(); } } @@ -2229,6 +2234,15 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mOccupied[i][j] = mTmpOccupied[i][j]; } } + + long screenId = mLauncher.getWorkspace().getIdForScreen(this); + int container = Favorites.CONTAINER_DESKTOP; + + if (mLauncher.isHotseatLayout(this)) { + screenId = -1; + container = Favorites.CONTAINER_HOTSEAT; + } + int childCount = mShortcutsAndWidgets.getChildCount(); for (int i = 0; i < childCount; i++) { View child = mShortcutsAndWidgets.getChildAt(i); @@ -2237,17 +2251,21 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // We do a null check here because the item info can be null in the case of the // AllApps button in the hotseat. if (info != null) { - if (info.cellX != lp.tmpCellX || info.cellY != lp.tmpCellY || - info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan) { - info.requiresDbUpdate = true; - } + final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX + || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan + || info.spanY != lp.cellVSpan); + info.cellX = lp.cellX = lp.tmpCellX; info.cellY = lp.cellY = lp.tmpCellY; info.spanX = lp.cellHSpan; info.spanY = lp.cellVSpan; + + if (requiresDbUpdate) { + LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, + info.cellX, info.cellY, info.spanX, info.spanY); + } } } - mLauncher.getWorkspace().updateItemLocationsInDatabase(this); } private void setUseTempCoords(boolean useTempCoords) { @@ -2835,10 +2853,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { // X coordinate of the view in the layout. @ViewDebug.ExportedProperty - int x; + public int x; // Y coordinate of the view in the layout. @ViewDebug.ExportedProperty - int y; + public int y; boolean dropped; @@ -2964,6 +2982,26 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied); } + /** + * Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing + * if necessary). + */ + public boolean hasReorderSolution(ItemInfo itemInfo) { + int[] cellPoint = new int[2]; + // Check for a solution starting at every cell. + for (int cellX = 0; cellX < getCountX(); cellX++) { + for (int cellY = 0; cellY < getCountY(); cellY++) { + cellToPoint(cellX, cellY, cellPoint); + if (findReorderSolution(cellPoint[0], cellPoint[1], itemInfo.minSpanX, + itemInfo.minSpanY, itemInfo.spanX, itemInfo.spanY, mDirectionVector, null, + true, new ItemConfiguration()).isSolution) { + return true; + } + } + } + return false; + } + public boolean isRegionVacant(int x, int y, int spanX, int spanY) { int x2 = x + spanX - 1; int y2 = y + spanY - 1; diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 2a0e203e6..3a79d94fa 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -23,6 +23,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.animation.AnimationUtils; +import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.FlingAnimation; import com.android.launcher3.util.Thunk; @@ -45,20 +46,20 @@ public class DeleteDropTarget extends ButtonDropTarget { setDrawable(R.drawable.ic_remove_launcher); } - public static boolean supportsDrop(Object info) { + public static boolean supportsDrop(ItemInfo info) { return (info instanceof ShortcutInfo) || (info instanceof LauncherAppWidgetInfo) || (info instanceof FolderInfo); } @Override - protected boolean supportsDrop(DragSource source, Object info) { + protected boolean supportsDrop(DragSource source, ItemInfo info) { return source.supportsDeleteDropTarget() && supportsDrop(info); } @Override @Thunk void completeDrop(DragObject d) { - ItemInfo item = (ItemInfo) d.dragInfo; + ItemInfo item = d.dragInfo; if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) { removeWorkspaceOrFolderItem(mLauncher, item, null); } @@ -80,7 +81,6 @@ public class DeleteDropTarget extends ButtonDropTarget { public void onFlingToDelete(final DragObject d, PointF vel) { // Don't highlight the icon as it's animating d.dragView.setColor(0); - d.dragView.updateInitialScaleToCurrentScale(); final DragLayer dragLayer = mLauncher.getDragLayer(); FlingAnimation fling = new FlingAnimation(d, vel, diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 774594fe2..69ef82683 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -280,6 +280,18 @@ public class DeviceProfile { return bounds; } + public Point getCellSize() { + Point result = new Point(); + // Since we are only concerned with the overall padding, layout direction does + // not matter. + Rect padding = getWorkspacePadding(false /* isLayoutRtl */ ); + result.x = calculateCellWidth(availableWidthPx - padding.left - padding.right, + inv.numColumns); + result.y = calculateCellHeight(availableHeightPx - padding.top - padding.bottom, + inv.numRows); + return result; + } + /** Returns the workspace padding in the specified orientation */ Rect getWorkspacePadding(boolean isLayoutRtl) { Rect searchBarBounds = getSearchBarBounds(isLayoutRtl); @@ -339,13 +351,13 @@ public class DeviceProfile { } // The rect returned will be extended to below the system ui that covers the workspace - Rect getHotseatRect() { + public boolean isInHotseatRect(int x, int y) { if (isVerticalBarLayout()) { - return new Rect(availableWidthPx - hotseatBarHeightPx, 0, - Integer.MAX_VALUE, availableHeightPx); + return (x >= (availableWidthPx - hotseatBarHeightPx)) + && (y >= 0) && (y <= availableHeightPx); } else { - return new Rect(0, availableHeightPx - hotseatBarHeightPx, - availableWidthPx, Integer.MAX_VALUE); + return (x >= 0) && (x <= availableWidthPx) + && (y >= (availableHeightPx - hotseatBarHeightPx)); } } @@ -360,7 +372,7 @@ public class DeviceProfile { * When {@code true}, hotseat is on the bottom row when in landscape mode. * If {@code false}, hotseat is on the right column when in landscape mode. */ - boolean isVerticalBarLayout() { + public boolean isVerticalBarLayout() { return isLandscape && transposeLayoutWithOrientation; } @@ -385,29 +397,15 @@ public class DeviceProfile { // Layout the search bar space View searchBar = launcher.getSearchDropTargetBar(); - lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); - if (hasVerticalBarLayout) { - // Vertical search bar space -- The search bar is fixed in the layout to be on the left - // of the screen regardless of RTL - lp.gravity = Gravity.LEFT; - lp.width = searchBarSpaceHeightPx; - - LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar); - targets.setOrientation(LinearLayout.VERTICAL); - FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams(); - targetsLp.gravity = Gravity.TOP; - targetsLp.height = LayoutParams.WRAP_CONTENT; - - } else { - // Horizontal search bar space - lp.gravity = Gravity.TOP; - lp.height = searchBarSpaceHeightPx; - - LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar); - targets.getLayoutParams().width = searchBarSpaceWidthPx; - } + lp = getDropTargetBarLayoutParams(hasVerticalBarLayout, searchBar, Gravity.TOP); searchBar.setLayoutParams(lp); + // Layout the app info bar space + View appInfoBar = launcher.getAppInfoDropTargetBar(); + lp = getDropTargetBarLayoutParams(hasVerticalBarLayout, appInfoBar, Gravity.BOTTOM); + lp.bottomMargin = hotseatBarHeightPx; + appInfoBar.setLayoutParams(lp); + // Layout the workspace PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace); lp = (FrameLayout.LayoutParams) workspace.getLayoutParams(); @@ -466,7 +464,6 @@ public class DeviceProfile { // Layout the Overview Mode ViewGroup overviewMode = launcher.getOverviewPanel(); if (overviewMode != null) { - int overviewButtonBarHeight = getOverviewModeButtonBarHeight(); lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; @@ -475,7 +472,7 @@ public class DeviceProfile { int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx; lp.width = Math.min(availableWidthPx, maxWidth); - lp.height = overviewButtonBarHeight; + lp.height = getOverviewModeButtonBarHeight(); overviewMode.setLayoutParams(lp); if (lp.width > totalItemWidth && visibleChildCount > 1) { @@ -504,6 +501,31 @@ public class DeviceProfile { } } + private FrameLayout.LayoutParams getDropTargetBarLayoutParams(boolean hasVerticalBarLayout, + View dropTargetBar, int verticalGravity) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) dropTargetBar.getLayoutParams(); + if (hasVerticalBarLayout) { + // Vertical drop target bar space -- The drop target bar is fixed in the layout to be on + // the left of the screen regardless of RTL + lp.gravity = Gravity.LEFT; + lp.width = searchBarSpaceHeightPx; + + LinearLayout targets = (LinearLayout) dropTargetBar.findViewById(R.id.drag_target_bar); + targets.setOrientation(LinearLayout.VERTICAL); + FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams(); + targetsLp.gravity = verticalGravity; + targetsLp.height = LayoutParams.WRAP_CONTENT; + } else { + // Horizontal drop target bar space + lp.gravity = verticalGravity; + lp.height = searchBarSpaceHeightPx; + + LinearLayout targets = (LinearLayout) dropTargetBar.findViewById(R.id.drag_target_bar); + targets.getLayoutParams().width = searchBarSpaceWidthPx; + } + return lp; + } + private int getCurrentWidth() { return isLandscape ? Math.max(widthPx, heightPx) diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 434059168..bab46d9f4 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import com.android.launcher3.dragndrop.DragView; + import android.graphics.PointF; import android.graphics.Rect; @@ -49,7 +51,7 @@ public interface DropTarget { public DragView dragView = null; /** The data associated with the object being dragged */ - public Object dragInfo = null; + public ItemInfo dragInfo = null; /** Where the drag originated */ public DragSource dragSource = null; diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java index c7b64ec7d..bf4551b26 100644 --- a/src/com/android/launcher3/ExtendedEditText.java +++ b/src/com/android/launcher3/ExtendedEditText.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.content.Context; import android.util.AttributeSet; +import android.view.DragEvent; import android.view.KeyEvent; import android.widget.EditText; @@ -62,4 +63,10 @@ public class ExtendedEditText extends EditText { } return super.onKeyPreIme(keyCode, event); } + + @Override + public boolean onDragEvent(DragEvent event) { + // We don't want this view to interfere with Launcher own drag and drop. + return false; + } } diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 6872d5b0a..099194ca5 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -22,6 +22,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.util.FocusLogic; import com.android.launcher3.util.Thunk; @@ -73,7 +74,7 @@ public class FocusHelper { } if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) { - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { throw new IllegalStateException("Parent of the focused item is not supported."); } else { return false; diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java index ecf93e4b3..2337f6eed 100644 --- a/src/com/android/launcher3/FocusIndicatorView.java +++ b/src/com/android/launcher3/FocusIndicatorView.java @@ -16,7 +16,7 @@ package com.android.launcher3; -import android.animation.ObjectAnimator; +import android.animation.Animator; import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Canvas; @@ -36,7 +36,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen private final int[] mIndicatorPos = new int[2]; private final int[] mTargetViewPos = new int[2]; - private ObjectAnimator mCurrentAnimation; + private Animator mCurrentAnimation; private ViewAnimState mTargetState; private View mLastFocusedView; @@ -98,12 +98,12 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen if (getAlpha() > MIN_VISIBLE_ALPHA) { mTargetState = nextState; - mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this, - PropertyValuesHolder.ofFloat(View.ALPHA, 1), - PropertyValuesHolder.ofFloat(View.TRANSLATION_X, mTargetState.x), - PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, mTargetState.y), - PropertyValuesHolder.ofFloat(View.SCALE_X, mTargetState.scaleX), - PropertyValuesHolder.ofFloat(View.SCALE_Y, mTargetState.scaleY)); + mCurrentAnimation = new LauncherViewPropertyAnimator(this) + .alpha(1) + .translationX(mTargetState.x) + .translationY(mTargetState.y) + .scaleX(mTargetState.scaleX) + .scaleY(mTargetState.scaleY); } else { applyState(nextState); mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this, diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 1499a2736..7f8917ccd 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -36,6 +36,7 @@ import android.text.Spannable; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; +import android.view.FocusFinder; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -52,11 +53,13 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.CellLayout.CellInfo; -import com.android.launcher3.DragController.DragListener; import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.UninstallDropTarget.UninstallSource; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragController.DragListener; +import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.UiThreadCircularReveal; @@ -316,7 +319,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (commit) { sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - String.format(getContext().getString(R.string.folder_renamed), newTitle)); + getContext().getString(R.string.folder_renamed, newTitle)); } // In order to clear the focus from the text field, we set the focus on ourself. This // ensures that every time the field is clicked, focus is gained, giving reliable behavior. @@ -356,11 +359,26 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } @Override + protected void onAttachedToWindow() { + // requestFocus() causes the focus onto the folder itself, which doesn't cause visual + // effect but the next arrow key can start the keyboard focus inside of the folder, not + // the folder itself. + requestFocus(); + super.onAttachedToWindow(); + } + + @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { // When the folder gets focus, we don't want to announce the list of items. return true; } + @Override + public View focusSearch(int direction) { + // When the folder is focused, further focus search should be within the folder contents. + return FocusFinder.getInstance().findNextFocus(this, null, direction); + } + /** * @return the FolderInfo object associated with this folder */ @@ -457,11 +475,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList positionAndSizeAsIcon(); centerAboutIcon(); - PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1); - PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f); - PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f); - final ObjectAnimator oa = - LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY); + final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 1, 1, 1); oa.setDuration(mExpandDuration); openFolderAnim = oa; @@ -484,8 +498,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList float transY = - 0.075f * (height / 2 - getPivotY()); setTranslationX(transX); setTranslationY(transY); - PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0); - PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0); + PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0); + PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0); Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty); drift.setDuration(mMaterialExpandDuration); @@ -564,6 +578,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList final boolean updateAnimationFlag = !mDragInProgress; openFolderAnim.addListener(new AnimatorListenerAdapter() { + @SuppressLint("InlinedApi") @Override public void onAnimationEnd(Animator animation) { mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION) @@ -607,7 +622,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } @Override - public void onDragStart(DragSource source, Object info, int dragAction) { } + public void onDragStart(DragSource source, ItemInfo info, int dragAction) { } @Override public void onDragEnd() { @@ -630,12 +645,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void animateClosed() { if (!(getParent() instanceof DragLayer)) return; - PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0); - PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f); - PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.9f); - final ObjectAnimator oa = - LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY); - + final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f); oa.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -656,7 +666,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public boolean acceptDrop(DragObject d) { - final ItemInfo item = (ItemInfo) d.dragInfo; + final ItemInfo item = d.dragInfo; final int itemType = item.itemType; return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) && @@ -901,7 +911,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public boolean supportsAppInfoDropTarget() { - return false; + return true; } @Override diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 8d534d2fe..bd61a6de5 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -43,6 +43,8 @@ import android.widget.TextView; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.FolderInfo.FolderListener; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -129,7 +131,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void init() { mLongPressHelper = new CheckLongPressHelper(this); - mStylusEventHelper = new StylusEventHelper(this); + mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @@ -171,8 +173,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { icon.setOnClickListener(launcher); icon.mInfo = folderInfo; icon.mLauncher = launcher; - icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format), - folderInfo.title)); + icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title)); Folder folder = Folder.fromXml(launcher); folder.setDragController(launcher.getDragController()); folder.setFolderIcon(icon); @@ -330,8 +331,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { !mFolder.isFull() && item != mInfo && !mInfo.opened); } - public boolean acceptDrop(Object dragInfo) { - final ItemInfo item = (ItemInfo) dragInfo; + public boolean acceptDrop(ItemInfo dragInfo) { + final ItemInfo item = dragInfo; return !mFolder.isDestroyed() && willAcceptItem(item); } @@ -339,8 +340,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { mInfo.add(item); } - public void onDragEnter(Object dragInfo) { - if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return; + public void onDragEnter(ItemInfo dragInfo) { + if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return; CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); CellLayout layout = (CellLayout) getParent().getParent(); mFolderRingAnimator.setCell(lp.cellX, lp.cellY); @@ -356,7 +357,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { // Workspace#onDropExternal. mOpenAlarm.setAlarm(ON_OPEN_DELAY); } - mDragInfo = (ItemInfo) dragInfo; + mDragInfo = dragInfo; } public void onDragOver(Object dragInfo) { @@ -712,8 +713,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { public void onTitleChanged(CharSequence title) { mFolderName.setText(title); - setContentDescription(String.format(getContext().getString(R.string.folder_name_format), - title)); + setContentDescription(getContext().getString(R.string.folder_name_format, title)); } @Override @@ -723,7 +723,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { boolean result = super.onTouchEvent(event); // Check for a stylus button press, if it occurs cancel any long press checks. - if (mStylusEventHelper.checkAndPerformStylusEvent(event)) { + if (mStylusEventHelper.onMotionEvent(event)) { mLongPressHelper.cancelLongPress(); return true; } diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index aea21c95b..6e0dcd421 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -22,7 +22,6 @@ import android.content.Context; import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; -import java.util.Arrays; /** * Represents a folder containing shortcuts or apps. @@ -140,7 +139,7 @@ public class FolderInfo extends ItemInfo { return "FolderInfo(id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX - + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")"; + + " spanY=" + spanY + ")"; } public boolean hasOption(int optionFlag) { diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index cc9c5738a..7aff8322e 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -30,6 +30,7 @@ import android.view.animation.OvershootInterpolator; import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; import com.android.launcher3.PageIndicator.PageMarkerResources; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -435,8 +436,7 @@ public class FolderPagedView extends PagedView { } public String getAccessibilityDescription() { - return String.format(getContext().getString(R.string.folder_opened), - mGridCountX, mGridCountY); + return getContext().getString(R.string.folder_opened, mGridCountX, mGridCountY); } /** diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 17fdeb1dc..a9cff5e65 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -17,8 +17,6 @@ package com.android.launcher3; import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.AttributeSet; diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 59ab8397d..bf8ee9d61 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -106,7 +106,6 @@ public class IconCache { private final BitmapFactory.Options mLowResOptions; private String mSystemState; - private Bitmap mLowResBitmap; private Canvas mLowResCanvas; private Paint mLowResPaint; @@ -117,6 +116,8 @@ public class IconCache { mLauncherApps = LauncherAppsCompat.getInstance(mContext); mIconDpi = inv.fillResIconDpi; mIconDb = new IconDB(context); + mLowResCanvas = new Canvas(); + mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); @@ -239,7 +240,7 @@ public class IconCache { long userSerial = mUserManager.getSerialNumberForUser(user); mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?", - new String[] {packageName + "/%", Long.toString(userSerial)}); + new String[]{packageName + "/%", Long.toString(userSerial)}); } public void updateDbIcons(Set<String> ignorePackagesForMainUser) { @@ -386,7 +387,8 @@ public class IconCache { entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry); - return newContentValues(entry.icon, entry.title.toString(), mActivityBgColor); + Bitmap lowResIcon = generateLowResIcon(entry.icon, mActivityBgColor); + return newContentValues(entry.icon, lowResIcon, entry.title.toString()); } /** @@ -623,17 +625,22 @@ public class IconCache { if (appInfo == null) { throw new NameNotFoundException("ApplicationInfo is null"); } + + // Load the full res icon for the application, but if useLowResIcon is set, then + // only keep the low resolution icon instead of the larger full-sized icon Drawable drawable = mUserManager.getBadgedDrawableForUser( appInfo.loadIcon(mPackageManager), user); - entry.icon = Utilities.createIconBitmap(drawable, mContext); + Bitmap icon = Utilities.createIconBitmap(drawable, mContext); + Bitmap lowResIcon = generateLowResIcon(icon, mPackageBgColor); entry.title = appInfo.loadLabel(mPackageManager); entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); - entry.isLowResIcon = false; + entry.icon = useLowResIcon ? lowResIcon : icon; + entry.isLowResIcon = useLowResIcon; // Add the icon in the DB here, since these do not get written during // package updates. ContentValues values = - newContentValues(entry.icon, entry.title.toString(), mPackageBgColor); + newContentValues(icon, lowResIcon, entry.title.toString()); addIconToDB(values, cacheKey.componentName, info, mUserManager.getSerialNumberForUser(user)); @@ -673,9 +680,9 @@ public class IconCache { // pass } - ContentValues values = newContentValues( - Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true), - label, Color.TRANSPARENT); + icon = Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true); + Bitmap lowResIcon = generateLowResIcon(icon, Color.TRANSPARENT); + ContentValues values = newContentValues(icon, lowResIcon, label); values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString()); values.put(IconDB.COLUMN_USER, userSerial); mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, @@ -841,34 +848,37 @@ public class IconCache { } } - private ContentValues newContentValues(Bitmap icon, String label, int lowResBackgroundColor) { + private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label) { ContentValues values = new ContentValues(); values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon)); + values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon)); values.put(IconDB.COLUMN_LABEL, label); values.put(IconDB.COLUMN_SYSTEM_STATE, mSystemState); + return values; + } + + /** + * Generates a new low-res icon given a high-res icon. + */ + private Bitmap generateLowResIcon(Bitmap icon, int lowResBackgroundColor) { if (lowResBackgroundColor == Color.TRANSPARENT) { - values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap( - Bitmap.createScaledBitmap(icon, - icon.getWidth() / LOW_RES_SCALE_FACTOR, - icon.getHeight() / LOW_RES_SCALE_FACTOR, true))); + return Bitmap.createScaledBitmap(icon, + icon.getWidth() / LOW_RES_SCALE_FACTOR, + icon.getHeight() / LOW_RES_SCALE_FACTOR, true); } else { + Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR, + icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565); synchronized (this) { - if (mLowResBitmap == null) { - mLowResBitmap = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR, - icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565); - mLowResCanvas = new Canvas(mLowResBitmap); - mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); - } mLowResCanvas.drawColor(lowResBackgroundColor); mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()), - new Rect(0, 0, mLowResBitmap.getWidth(), mLowResBitmap.getHeight()), + new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()), mLowResPaint); - values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(mLowResBitmap)); + mLowResCanvas.setBitmap(null); } + return lowResIcon; } - return values; } private static Bitmap loadIconNoResize(Cursor c, int iconIndex, BitmapFactory.Options options) { diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index d93cdcc1b..d444640e6 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -20,9 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.util.AttributeSet; -import com.android.launcher3.compat.UserHandleCompat; - -public class InfoDropTarget extends ButtonDropTarget { +public class InfoDropTarget extends UninstallDropTarget { public InfoDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -41,7 +39,10 @@ public class InfoDropTarget extends ButtonDropTarget { setDrawable(R.drawable.ic_info_launcher); } - public static void startDetailsActivityForInfo(Object info, Launcher launcher) { + /** + * @return Whether the activity was started. + */ + public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher) { ComponentName componentName = null; if (info instanceof AppInfo) { componentName = ((AppInfo) info).componentName; @@ -49,30 +50,28 @@ public class InfoDropTarget extends ButtonDropTarget { componentName = ((ShortcutInfo) info).intent.getComponent(); } else if (info instanceof PendingAddItemInfo) { componentName = ((PendingAddItemInfo) info).componentName; + } else if (info instanceof LauncherAppWidgetInfo) { + componentName = ((LauncherAppWidgetInfo) info).providerName; } - final UserHandleCompat user; - if (info instanceof ItemInfo) { - user = ((ItemInfo) info).user; - } else { - user = UserHandleCompat.myUserHandle(); - } - if (componentName != null) { - launcher.startApplicationDetailsActivity(componentName, user); + launcher.startApplicationDetailsActivity(componentName, info.user); + return true; } + return false; } @Override - protected boolean supportsDrop(DragSource source, Object info) { - return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info); + protected boolean startActivityWithUninstallAffordance(DragObject d) { + return startDetailsActivityForInfo(d.dragInfo, mLauncher); } - public static boolean supportsDrop(Context context, Object info) { - return info instanceof AppInfo || info instanceof PendingAddItemInfo; + @Override + protected boolean supportsDrop(DragSource source, ItemInfo info) { + return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info); } - @Override - void completeDrop(DragObject d) { - startDetailsActivityForInfo(d.dragInfo, mLauncher); + public static boolean supportsDrop(Context context, ItemInfo info) { + return info instanceof AppInfo || info instanceof ShortcutInfo + || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo; } } diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index f7e0ea488..aa5a18d3c 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -24,8 +24,6 @@ import android.graphics.Bitmap; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; -import java.util.Arrays; - /** * Represents an item in the launcher. */ @@ -35,14 +33,14 @@ public class ItemInfo { * Intent extra to store the profile. Format: UserHandle */ static final String EXTRA_PROFILE = "profile"; - + public static final int NO_ID = -1; - + /** * The id in the settings database for this item */ public long id = NO_ID; - + /** * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION}, * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT}, @@ -50,20 +48,20 @@ public class ItemInfo { * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}. */ public int itemType; - + /** - * The id of the container that holds this item. For the desktop, this will be + * The id of the container that holds this item. For the desktop, this will be * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. For the all applications folder it * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders * it will be the id of the folder. */ public long container = NO_ID; - + /** * Iindicates the screen in which the shortcut appears. */ public long screenId = -1; - + /** * Indicates the X position of the associated cell. */ @@ -100,11 +98,6 @@ public class ItemInfo { public int rank = 0; /** - * Indicates that this item needs to be updated in the db - */ - public boolean requiresDbUpdate = false; - - /** * Title of the item */ public CharSequence title; @@ -114,11 +107,6 @@ public class ItemInfo { */ public CharSequence contentDescription; - /** - * The position of the item in a drag-and-drop operation. - */ - public int[] dropPos = null; - public UserHandleCompat user; public ItemInfo() { @@ -146,18 +134,11 @@ public class ItemInfo { } public Intent getIntent() { - throw new RuntimeException("Unexpected Intent"); + return null; } - /** - * Write the fields of this item to the DB - * - * @param context A context object to use for getting UserManagerCompat - * @param values - */ - - void onAddToDatabase(Context context, ContentValues values) { - values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType); + public void writeToValues(ContentValues values) { + values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType); values.put(LauncherSettings.Favorites.CONTAINER, container); values.put(LauncherSettings.Favorites.SCREEN, screenId); values.put(LauncherSettings.Favorites.CELLX, cellX); @@ -165,6 +146,27 @@ public class ItemInfo { values.put(LauncherSettings.Favorites.SPANX, spanX); values.put(LauncherSettings.Favorites.SPANY, spanY); values.put(LauncherSettings.Favorites.RANK, rank); + } + + public void readFromValues(ContentValues values) { + itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE); + container = values.getAsLong(LauncherSettings.Favorites.CONTAINER); + screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN); + cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX); + cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY); + spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX); + spanY = values.getAsInteger(LauncherSettings.Favorites.SPANY); + rank = values.getAsInteger(LauncherSettings.Favorites.RANK); + } + + /** + * Write the fields of this item to the DB + * + * @param context A context object to use for getting UserManagerCompat + * @param values + */ + void onAddToDatabase(Context context, ContentValues values) { + writeToValues(values); long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user); values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber); @@ -194,7 +196,6 @@ public class ItemInfo { public String toString() { return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX - + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) - + " user=" + user + ")"; + + " spanY=" + spanY + " user=" + user + ")"; } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 84e6b997a..40c6df145 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -20,7 +20,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -36,6 +35,7 @@ import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; +import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -71,10 +71,8 @@ import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; import android.view.Display; -import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MotionEvent; import android.view.Surface; @@ -90,7 +88,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.animation.OvershootInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -103,10 +100,16 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LongArrayMap; +import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.ViewOnDrawExecutor; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; @@ -123,7 +126,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; /** * Default launcher application. @@ -131,7 +133,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener { - static final String TAG = "Launcher"; + public static final String TAG = "Launcher"; static final boolean LOGD = false; static final boolean PROFILE_STARTUP = false; @@ -162,8 +164,6 @@ public class Launcher extends Activity */ protected static final int REQUEST_LAST = 100; - static final int SCREEN_COUNT = 5; - // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] static final String DUMP_STATE_PROPERTY = "launcher_dump_state"; @@ -176,24 +176,12 @@ public class Launcher extends Activity private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; // Type: int private static final String RUNTIME_STATE = "launcher.state"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y"; + // Type: Content Values / parcelable + private static final String RUNTIME_STATE_PENDING_ADD_ITEM = "launcher.add_item"; // Type: parcelable private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info"; // Type: parcelable private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id"; - // Type: int[] - private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids"; static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed"; static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed"; @@ -202,16 +190,14 @@ public class Launcher extends Activity static final String ACTION_FIRST_LOAD_COMPLETE = "com.android.launcher3.action.FIRST_LOAD_COMPLETE"; - public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem"; - public static final boolean SHOW_WEIGHT_WATCHER_DEFAULT = false; - private static final String QSB_WIDGET_ID = "qsb_widget_id"; private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider"; public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data"; /** The different states that Launcher can be in. */ - enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED } + enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED, + WIDGETS, WIDGETS_SPRING_LOADED } @Thunk State mState = State.WORKSPACE; @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation; @@ -227,9 +213,6 @@ public class Launcher extends Activity private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; private static final int ACTIVITY_START_DELAY = 1000; - private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>(); - private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); - // How long to wait before the new-shortcut animation automatically pans the workspace private static int NEW_APPS_PAGE_MOVE_DELAY = 500; private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; @@ -238,19 +221,18 @@ public class Launcher extends Activity private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); - private LayoutInflater mInflater; - @Thunk Workspace mWorkspace; private View mLauncherView; private View mPageIndicators; @Thunk DragLayer mDragLayer; private DragController mDragController; - private View mWeightWatcher; + + public View mWeightWatcher; private AppWidgetManagerCompat mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; - @Thunk ItemInfo mPendingAddInfo = new ItemInfo(); + @Thunk final ItemInfo mPendingAddInfo = new ItemInfo(); private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo; private int mPendingAddWidgetId = -1; @@ -263,6 +245,7 @@ public class Launcher extends Activity private View mWidgetsButton; private SearchDropTargetBar mSearchDropTargetBar; + private AppInfoDropTargetBar mAppInfoDropTargetBar; // Main container view for the all apps screen. @Thunk AllAppsContainerView mAppsView; @@ -271,7 +254,6 @@ public class Launcher extends Activity @Thunk WidgetsContainerView mWidgetsView; @Thunk WidgetsModel mWidgetsModel; - private boolean mAutoAdvanceRunning = false; private AppWidgetHostView mQsb; private Bundle mSavedState; @@ -291,8 +273,7 @@ public class Launcher extends Activity private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>(); private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>(); - - private Bundle mSavedInstanceState; + private ViewOnDrawExecutor mPendingExecutor; private LauncherModel mModel; private IconCache mIconCache; @@ -309,16 +290,17 @@ public class Launcher extends Activity // Related to the auto-advancing of widgets private final int ADVANCE_MSG = 1; - private final int mAdvanceInterval = 20000; - private final int mAdvanceStagger = 250; + private static final int ADVANCE_INTERVAL = 20000; + private static final int ADVANCE_STAGGER = 250; + + private boolean mAutoAdvanceRunning = false; private long mAutoAdvanceSentTime; private long mAutoAdvanceTimeLeft = -1; - @Thunk HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance = - new HashMap<View, AppWidgetProviderInfo>(); + @Thunk HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance = new HashMap<>(); // Determines how long to wait after a rotation before restoring the screen orientation to // match the sensor state. - private final int mRestoreScreenOrientationDelay = 500; + private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500; @Thunk Drawable mWorkspaceBackgroundDrawable; @@ -355,10 +337,9 @@ public class Launcher extends Activity protected static HashMap<String, CustomAppWidget> sCustomAppWidgets = new HashMap<String, CustomAppWidget>(); - private static final boolean ENABLE_CUSTOM_WIDGET_TEST = false; static { - if (ENABLE_CUSTOM_WIDGET_TEST) { - sCustomAppWidgets.put(DummyWidget.class.getName(), new DummyWidget()); + if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) { + TestingUtils.addDummyWidget(sCustomAppWidgets); } } @@ -440,7 +421,6 @@ public class Launcher extends Activity mIconCache = app.getIconCache(); mDragController = new DragController(this); - mInflater = getLayoutInflater(); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this); mStats = new Stats(this); @@ -497,7 +477,7 @@ public class Launcher extends Activity // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { - mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false); + mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); } // On large interfaces, or on devices that a user has specifically enabled screen rotation, @@ -645,44 +625,18 @@ public class Launcher extends Activity return mStats; } - public LayoutInflater getInflater() { - return mInflater; - } - public boolean isDraggingEnabled() { // We prevent dragging when we are loading the workspace as it is possible to pick up a view // that is subsequently removed from the workspace in startBinding(). return !isWorkspaceLoading(); } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public static int generateViewId() { - if (Utilities.ATLEAST_JB_MR1) { - return View.generateViewId(); - } else { - // View.generateViewId() is not available. The following fallback logic is a copy - // of its implementation. - for (;;) { - final int result = sNextGeneratedId.get(); - // aapt-generated IDs have the high byte nonzero; clamp to the range under that. - int newValue = result + 1; - if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. - if (sNextGeneratedId.compareAndSet(result, newValue)) { - return result; - } - } - } - } - public int getViewIdForItem(ItemInfo info) { - // This cast is safe given the > 2B range for int. - int itemId = (int) info.id; - if (mItemIdToViewId.containsKey(itemId)) { - return mItemIdToViewId.get(itemId); - } - int viewId = generateViewId(); - mItemIdToViewId.put(itemId, viewId); - return viewId; + // aapt-generated IDs have the high byte nonzero; clamp to the range under that. + // This cast is safe as long as the id < 0x00FFFFFF + // Since we jail all the dynamically generated views, there should be no clashes + // with any other views. + return (int) info.id; } /** @@ -751,7 +705,7 @@ public class Launcher extends Activity } else if (requestCode == REQUEST_PICK_WALLPAPER) { if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) { // User could have free-scrolled between pages before picking a wallpaper; make sure - // we move to the closest one now to avoid visual jump. + // we move to the closest one now. mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen()); showWorkspace(false); } @@ -837,24 +791,22 @@ public class Launcher extends Activity return; } - // The pattern used here is that a user PICKs a specific application, - // which, depending on the target, might need to CREATE the actual target. - - // For example, the user would PICK_SHORTCUT for "Music playlist", and we - // launch over to the Music app to actually CREATE_SHORTCUT. - if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) { - final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1, - mPendingAddInfo); - if (isWorkspaceLocked()) { - sPendingAddItem = args; - } else { - completeAdd(args); + if (requestCode == REQUEST_CREATE_SHORTCUT) { + // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT. + if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) { + final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1, + mPendingAddInfo); + if (isWorkspaceLocked()) { + sPendingAddItem = args; + } else { + completeAdd(args); + mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); + } + } else if (resultCode == RESULT_CANCELED) { mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } - } else if (resultCode == RESULT_CANCELED) { - mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, - ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } mDragLayer.clearAnimatedView(); @@ -1001,12 +953,6 @@ public class Launcher extends Activity mPaused = false; if (mRestoring || mOnResumeNeedsLoad) { setWorkspaceLoading(true); - - // If we're starting binding all over again, clear any bind calls we'd postponed in - // the past (see waitUntilResume) -- we don't need them since we're starting binding - // from scratch again - mBindOnResumeCallbacks.clear(); - mModel.startLoader(PagedView.INVALID_RESTORE_PAGE); mRestoring = false; mOnResumeNeedsLoad = false; @@ -1336,7 +1282,6 @@ public class Launcher extends Activity * * @param savedState The previous state. */ - @SuppressWarnings("unchecked") private void restoreState(Bundle savedState) { if (savedState == null) { return; @@ -1353,16 +1298,9 @@ public class Launcher extends Activity mWorkspace.setRestorePage(currentScreen); } - final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1); - final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1); - - if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) { - mPendingAddInfo.container = pendingAddContainer; - mPendingAddInfo.screenId = pendingAddScreen; - mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); - mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); - mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); - mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); + ContentValues itemValues = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_ITEM); + if (itemValues != null) { + mPendingAddInfo.readFromValues(itemValues); AppWidgetProviderInfo info = savedState.getParcelable( RUNTIME_STATE_PENDING_ADD_WIDGET_INFO); mPendingAddWidgetInfo = info == null ? @@ -1372,9 +1310,6 @@ public class Launcher extends Activity setWaitingForResult(true); mRestoring = true; } - - mItemIdToViewId = (HashMap<Integer, Integer>) - savedState.getSerializable(RUNTIME_STATE_VIEW_IDS); } /** @@ -1459,9 +1394,12 @@ public class Launcher extends Activity mWorkspace.setup(dragController); dragController.addDragListener(mWorkspace); - // Get the search/delete bar + // Get the search/delete/uninstall bar mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.search_drop_target_bar); + // Get the app info bar + mAppInfoDropTargetBar = (AppInfoDropTargetBar) + mDragLayer.findViewById(R.id.app_info_drop_target_bar); // Setup Apps and Widgets mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view); @@ -1474,27 +1412,18 @@ public class Launcher extends Activity // Setup the drag controller (drop targets have to be added in reverse order in priority) dragController.setDragScoller(mWorkspace); - dragController.setScrollView(mDragLayer); dragController.setMoveTarget(mWorkspace); dragController.addDropTarget(mWorkspace); if (mSearchDropTargetBar != null) { mSearchDropTargetBar.setup(this, dragController); mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); } + if (mAppInfoDropTargetBar != null) { + mAppInfoDropTargetBar.setup(this, dragController); + } - if (getResources().getBoolean(R.bool.debug_memory_enabled)) { - Log.v(TAG, "adding WeightWatcher"); - mWeightWatcher = new WeightWatcher(this); - mWeightWatcher.setAlpha(0.5f); - ((FrameLayout) mLauncherView).addView(mWeightWatcher, - new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.WRAP_CONTENT, - Gravity.BOTTOM) - ); - - boolean show = shouldShowWeightWatcher(); - mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE); + if (TestingUtils.MEMORY_DUMP_ENABLED) { + TestingUtils.addWeightWatcher(this); } } @@ -1531,7 +1460,7 @@ public class Launcher extends Activity * @return A View inflated from layoutResId. */ public View createShortcut(ViewGroup parent, ShortcutInfo info) { - BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon, + BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon, parent, false); favorite.applyFromShortcutInfo(info, mIconCache); favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx); @@ -1548,7 +1477,6 @@ public class Launcher extends Activity private void completeAddShortcut(Intent data, long container, long screenId, int cellX, int cellY) { int[] cellXY = mTmpAddItemCellCoordinates; - int[] touchXY = mPendingAddInfo.dropPos; CellLayout layout = getCellLayout(container, screenId); ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data); @@ -1575,10 +1503,6 @@ public class Launcher extends Activity true)) { return; } - } else if (touchXY != null) { - // when dragging and dropping, just find the closest free spot - int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY); - foundCellSpan = (result != null); } else { foundCellSpan = layout.findCellForSpan(cellXY, 1, 1); } @@ -1785,11 +1709,11 @@ public class Launcher extends Activity if (autoAdvanceRunning != mAutoAdvanceRunning) { mAutoAdvanceRunning = autoAdvanceRunning; if (autoAdvanceRunning) { - long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft; + long delay = mAutoAdvanceTimeLeft == -1 ? ADVANCE_INTERVAL : mAutoAdvanceTimeLeft; sendAdvanceMessage(delay); } else { if (!mWidgetsToAdvance.isEmpty()) { - mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval - + mAutoAdvanceTimeLeft = Math.max(0, ADVANCE_INTERVAL - (System.currentTimeMillis() - mAutoAdvanceSentTime)); } mHandler.removeMessages(ADVANCE_MSG); @@ -1806,7 +1730,7 @@ public class Launcher extends Activity int i = 0; for (View key: mWidgetsToAdvance.keySet()) { final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId); - final int delay = mAdvanceStagger * i; + final int delay = ADVANCE_STAGGER * i; if (v instanceof Advanceable) { mHandler.postDelayed(new Runnable() { public void run() { @@ -1816,7 +1740,7 @@ public class Launcher extends Activity } i++; } - sendAdvanceMessage(mAdvanceInterval); + sendAdvanceMessage(ADVANCE_INTERVAL); } return true; } @@ -1872,6 +1796,10 @@ public class Launcher extends Activity return mSearchDropTargetBar; } + public AppInfoDropTargetBar getAppInfoDropTargetBar() { + return mAppInfoDropTargetBar; + } + public LauncherAppWidgetHost getAppWidgetHost() { return mAppWidgetHost; } @@ -2006,19 +1934,15 @@ public class Launcher extends Activity if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 && mWaitingForResult) { - outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container); - outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId); - outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX); - outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY); - outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX); - outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY); + ContentValues itemValues = new ContentValues(); + mPendingAddInfo.writeToValues(itemValues); + outState.putParcelable(RUNTIME_STATE_PENDING_ADD_ITEM, itemValues); outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo); outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId); } // Save the current widgets tray? // TODO(hyunyoungs) - outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId); if (mLauncherCallbacks != null) { mLauncherCallbacks.onSaveInstanceState(outState); @@ -2253,7 +2177,6 @@ public class Launcher extends Activity mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1; mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1; mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1; - mPendingAddInfo.dropPos = null; } void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final @@ -2325,7 +2248,6 @@ public class Launcher extends Activity resetAddInfo(); mPendingAddInfo.container = container; mPendingAddInfo.screenId = screenId; - mPendingAddInfo.dropPos = null; if (cell != null) { mPendingAddInfo.cellX = cell[0]; @@ -2349,7 +2271,6 @@ public class Launcher extends Activity resetAddInfo(); mPendingAddInfo.container = info.container = container; mPendingAddInfo.screenId = info.screenId = screenId; - mPendingAddInfo.dropPos = null; mPendingAddInfo.minSpanX = info.minSpanX; mPendingAddInfo.minSpanY = info.minSpanY; @@ -2570,8 +2491,10 @@ public class Launcher extends Activity if (v instanceof CellLayout) { if (mWorkspace.isInOverviewMode()) { - showWorkspace(mWorkspace.indexOfChild(v), true); + mWorkspace.snapToPageFromOverView(mWorkspace.indexOfChild(v)); + showWorkspace(true); } + return; } Object tag = v.getTag(); @@ -2702,21 +2625,6 @@ public class Launcher extends Activity return; } - final Intent intent = shortcut.intent; - - // Check for special shortcuts - if (intent.getComponent() != null) { - final String shortcutClass = intent.getComponent().getClassName(); - - if (shortcutClass.equals(MemoryDumpActivity.class.getName())) { - MemoryDumpActivity.startDump(this); - return; - } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) { - toggleShowWeightWatcher(); - return; - } - } - // Check for abandoned promise if ((v instanceof BubbleTextView) && shortcut.isPromise() @@ -3102,10 +3010,6 @@ public class Launcher extends Activity private void growAndFadeOutFolderIcon(FolderIcon fi) { if (fi == null) return; - PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0); - PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f); - PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f); - FolderInfo info = (FolderInfo) fi.getTag(); if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { CellLayout cl = (CellLayout) fi.getParent().getParent(); @@ -3117,8 +3021,8 @@ public class Launcher extends Activity copyFolderIconToImage(fi); fi.setVisibility(View.INVISIBLE); - ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha, - scaleX, scaleY); + ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale( + mFolderIconImageView, 0, 1.5f, 1.5f); if (Utilities.ATLEAST_LOLLIPOP) { oa.setInterpolator(new LogDecelerateInterpolator(100, 0)); } @@ -3128,17 +3032,12 @@ public class Launcher extends Activity private void shrinkAndFadeInFolderIcon(final FolderIcon fi) { if (fi == null) return; - PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f); - PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f); - PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f); - final CellLayout cl = (CellLayout) fi.getParent().getParent(); // We remove and re-draw the FolderIcon in-case it has changed mDragLayer.removeView(mFolderIconImageView); copyFolderIconToImage(fi); - ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha, - scaleX, scaleY); + ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(mFolderIconImageView, 1, 1, 1); oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); oa.addListener(new AnimatorListenerAdapter() { @Override @@ -3357,38 +3256,17 @@ public class Launcher extends Activity } } - /** - * @return whether or not the Launcher state changed. - */ public boolean showWorkspace(boolean animated) { - return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null); + return showWorkspace(animated, null); } - /** - * @return whether or not the Launcher state changed. - */ public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) { - return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, - onCompleteRunnable); - } - - /** - * @return whether or not the Launcher state changed. - */ - protected boolean showWorkspace(int snapToPage, boolean animated) { - return showWorkspace(snapToPage, animated, null); - } - - /** - * @return whether or not the Launcher state changed. - */ - boolean showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) { boolean changed = mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL; if (changed) { mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), - Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable); + Workspace.State.NORMAL, animated, onCompleteRunnable); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { @@ -3414,9 +3292,7 @@ public class Launcher extends Activity void showOverviewMode(boolean animated) { mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), - Workspace.State.OVERVIEW, - WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, - null /* onCompleteRunnable */); + Workspace.State.OVERVIEW, animated, null /* onCompleteRunnable */); mState = State.WORKSPACE; } @@ -3493,31 +3369,48 @@ public class Launcher extends Activity * Updates the workspace and interaction state on state change, and return the animation to this * new state. */ - public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage, + public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, boolean animated, HashMap<View, Integer> layerViews) { Workspace.State fromState = mWorkspace.getState(); - Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews); + Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews); updateInteraction(fromState, toState); return anim; } + public void onLauncherClingShown() { + // When a launcher cling appears, it should cover the underlying layers, so their focus + // should be blocked. + if (mDragLayer.getDescendantFocusability() != ViewGroup.FOCUS_BLOCK_DESCENDANTS) { + mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + } + } + + public void onLauncherClingDismissed() { + mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); + } + public void enterSpringLoadedDragMode() { if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name())); - if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED || - mState == State.WIDGETS_SPRING_LOADED) { + if (isStateSpringLoaded()) { return; } mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), - Workspace.State.SPRING_LOADED, - WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */, + Workspace.State.SPRING_LOADED, true /* animated */, null /* onCompleteRunnable */); - mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; + + if (isAppsViewVisible()) { + mState = State.APPS_SPRING_LOADED; + } else if (isWidgetsViewVisible()) { + mState = State.WIDGETS_SPRING_LOADED; + } else { + mState = State.WORKSPACE_SPRING_LOADED; + } } public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { - if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; + if (!isStateSpringLoaded()) return; mHandler.postDelayed(new Runnable() { @Override @@ -3537,12 +3430,19 @@ public class Launcher extends Activity }, delay); } + private boolean isStateSpringLoaded() { + return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED + || mState == State.WIDGETS_SPRING_LOADED; + } + void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { showAppsView(true /* animated */, false /* resetListToTop */, false /* updatePredictedApps */, false /* focusSearchBar */); } else if (mState == State.WIDGETS_SPRING_LOADED) { showWidgetsView(true, false); + } else if (mState == State.WORKSPACE_SPRING_LOADED) { + showWorkspace(true); } } @@ -3731,7 +3631,20 @@ public class Launcher extends Activity if (mWorkspace != null) { return mWorkspace.getCurrentPage(); } else { - return SCREEN_COUNT / 2; + return 0; + } + } + + /** + * Clear any pending bind callbacks. This is called when is loader is planning to + * perform a full rebind from scratch. + */ + @Override + public void clearPendingBinds() { + mBindOnResumeCallbacks.clear(); + if (mPendingExecutor != null) { + mPendingExecutor.markCompleted(); + mPendingExecutor = null; } } @@ -3743,11 +3656,6 @@ public class Launcher extends Activity public void startBinding() { setWorkspaceLoading(true); - // If we're starting binding all over again, clear any bind calls we'd postponed in - // the past (see waitUntilResume) -- we don't need them since we're starting binding - // from scratch again - mBindOnResumeCallbacks.clear(); - // Clear the workspace because it's going to be rebound mWorkspace.clearDropTargets(); mWorkspace.removeAllWorkspaceScreens(); @@ -3783,30 +3691,6 @@ public class Launcher extends Activity } } - private boolean shouldShowWeightWatcher() { - String spKey = LauncherAppState.getSharedPreferencesKey(); - SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); - boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, SHOW_WEIGHT_WATCHER_DEFAULT); - - return show; - } - - private void toggleShowWeightWatcher() { - String spKey = LauncherAppState.getSharedPreferencesKey(); - SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); - boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, true); - - show = !show; - - SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(SHOW_WEIGHT_WATCHER, show); - editor.commit(); - - if (mWeightWatcher != null) { - mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE); - } - } - public void bindAppsAdded(final ArrayList<Long> newScreens, final ArrayList<ItemInfo> addNotAnimated, final ArrayList<ItemInfo> addAnimated, @@ -3892,7 +3776,7 @@ public class Launcher extends Activity Object tag = v.getTag(); String desc = "Collision while binding workspace item: " + item + ". Collides with " + tag; - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { throw (new RuntimeException(desc)); } else { Log.d(TAG, desc); @@ -4112,6 +3996,21 @@ public class Launcher extends Activity mSynchronouslyBoundPages.add(page); } + @Override + public void executeOnNextDraw(ViewOnDrawExecutor executor) { + if (mPendingExecutor != null) { + mPendingExecutor.markCompleted(); + } + mPendingExecutor = executor; + executor.attachTo(this); + } + + public void clearPendingExecutor(ViewOnDrawExecutor executor) { + if (mPendingExecutor == executor) { + mPendingExecutor = null; + } + } + /** * Callback saying that there aren't any more items to bind. * @@ -4188,10 +4087,7 @@ public class Launcher extends Activity } private ValueAnimator createNewAppBounceAnimation(View v, int i) { - ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v, - PropertyValuesHolder.ofFloat("alpha", 1f), - PropertyValuesHolder.ofFloat("scaleX", 1f), - PropertyValuesHolder.ofFloat("scaleY", 1f)); + ValueAnimator bounceAnim = LauncherAnimUtils.ofViewAlphaAndScale(v, 1, 1, 1); bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION)); @@ -4450,7 +4346,7 @@ public class Launcher extends Activity public void run() { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } - }, mRestoreScreenOrientationDelay); + }, RESTORE_SCREEN_ORIENTATION_DELAY); } } } @@ -4717,7 +4613,6 @@ public class Launcher extends Activity Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading); Log.d(TAG, "mRestoring=" + mRestoring); Log.d(TAG, "mWaitingForResult=" + mWaitingForResult); - Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState); Log.d(TAG, "sFolders.size=" + sFolders.size()); mModel.dumpState(); // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState(); diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 6a248a332..853c2ec6b 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -21,14 +21,10 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; -import android.annotation.TargetApi; -import android.os.Build; +import android.util.Property; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.ViewTreeObserver; -import com.android.launcher3.util.UiThreadCircularReveal; - import java.util.HashSet; import java.util.WeakHashMap; @@ -102,42 +98,32 @@ public class LauncherAnimUtils { return anim; } - public static ObjectAnimator ofFloat(View target, String propertyName, float... values) { - ObjectAnimator anim = new ObjectAnimator(); - anim.setTarget(target); - anim.setPropertyName(propertyName); - anim.setFloatValues(values); + public static ObjectAnimator ofFloat(View target, Property<View, Float> property, + float... values) { + ObjectAnimator anim = ObjectAnimator.ofFloat(target, property, values); cancelOnDestroyActivity(anim); new FirstFrameAnimatorHelper(anim, target); return anim; } + public static ObjectAnimator ofViewAlphaAndScale(View target, + float alpha, float scaleX, float scaleY) { + return ofPropertyValuesHolder(target, + PropertyValuesHolder.ofFloat(View.ALPHA, alpha), + PropertyValuesHolder.ofFloat(View.SCALE_X, scaleX), + PropertyValuesHolder.ofFloat(View.SCALE_Y, scaleY)); + } + public static ObjectAnimator ofPropertyValuesHolder(View target, PropertyValuesHolder... values) { - ObjectAnimator anim = new ObjectAnimator(); - anim.setTarget(target); - anim.setValues(values); - cancelOnDestroyActivity(anim); - new FirstFrameAnimatorHelper(anim, target); - return anim; + return ofPropertyValuesHolder(target, target, values); } public static ObjectAnimator ofPropertyValuesHolder(Object target, View view, PropertyValuesHolder... values) { - ObjectAnimator anim = new ObjectAnimator(); - anim.setTarget(target); - anim.setValues(values); + ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(target, values); cancelOnDestroyActivity(anim); new FirstFrameAnimatorHelper(anim, view); return anim; } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public static ValueAnimator createCircularReveal(View view, int centerX, - int centerY, float startRadius, float endRadius) { - ValueAnimator anim = UiThreadCircularReveal.createCircularReveal(view, centerX, - centerY, startRadius, endRadius); - new FirstFrameAnimatorHelper(anim, view); - return anim; - } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index d87ad67e5..3b207a9cf 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -17,16 +17,17 @@ package com.android.launcher3; import android.app.SearchManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.UserManager; import android.util.Log; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; @@ -34,12 +35,11 @@ import java.lang.ref.WeakReference; public class LauncherAppState { private final AppFilter mAppFilter; - private final BuildInfo mBuildInfo; @Thunk final LauncherModel mModel; private final IconCache mIconCache; private final WidgetPreviewLoader mWidgetCache; - private boolean mWallpaperChangedSinceLastCheck; + @Thunk boolean mWallpaperChangedSinceLastCheck; private static WeakReference<LauncherProvider> sLauncherProvider; private static Context sContext; @@ -79,8 +79,8 @@ public class LauncherAppState { Log.v(Launcher.TAG, "LauncherAppState inited"); - if (sContext.getResources().getBoolean(R.bool.debug_memory_enabled)) { - MemoryTracker.startTrackingMe(sContext, "L"); + if (TestingUtils.MEMORY_DUMP_ENABLED) { + TestingUtils.startTrackingMemory(sContext); } mInvariantDeviceProfile = new InvariantDeviceProfile(sContext); @@ -88,7 +88,6 @@ public class LauncherAppState { mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache); mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); - mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class)); mModel = new LauncherModel(this, mIconCache, mAppFilter); LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel); @@ -103,6 +102,16 @@ public class LauncherAppState { sContext.registerReceiver(mModel, filter); UserManagerCompat.getInstance(sContext).enableAndResetCache(); + + if (!Utilities.ATLEAST_KITKAT) { + sContext.registerReceiver(new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + mWallpaperChangedSinceLastCheck = true; + } + }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)); + } } /** @@ -159,10 +168,6 @@ public class LauncherAppState { public WidgetPreviewLoader getWidgetCache() { return mWidgetCache; } - - public void onWallpaperChanged() { - mWallpaperChangedSinceLastCheck = true; - } public boolean hasWallpaperChangedSinceLastCheck() { boolean result = mWallpaperChangedSinceLastCheck; @@ -173,8 +178,4 @@ public class LauncherAppState { public InvariantDeviceProfile getInvariantDeviceProfile() { return mInvariantDeviceProfile; } - - public static boolean isDogfoodBuild() { - return getInstance().mBuildInfo.isDogfoodBuild(); - } } diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index cf461a5b8..01332fb9e 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -26,7 +26,8 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.RemoteViews; -import com.android.launcher3.DragLayer.TouchCompleteListener; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener; /** * {@inheritDoc} @@ -47,7 +48,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc super(context); mContext = context; mLongPressHelper = new CheckLongPressHelper(this); - mStylusEventHelper = new StylusEventHelper(this); + mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mDragLayer = ((Launcher) context).getDragLayer(); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); @@ -93,7 +94,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc // Watch for longpress or stylus button press events at this level to // make sure users can always pick up this widget - if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + if (mStylusEventHelper.onMotionEvent(ev)) { mLongPressHelper.cancelLongPress(); return true; } diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index 18fe8ef86..43d05a659 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -27,9 +27,11 @@ import android.os.Build; import android.os.Bundle; import android.os.UserManager; import android.provider.Settings; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; +import android.view.View.OnKeyListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewTreeObserver.OnGlobalLayoutListener; @@ -37,7 +39,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.launcher3.util.Thunk; -class LauncherClings implements OnClickListener { +class LauncherClings implements OnClickListener, OnKeyListener { private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed"; private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed"; @@ -85,6 +87,20 @@ class LauncherClings implements OnClickListener { } } + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.isPrintingKey()) { + // Should ignore all printing keys, otherwise they come to the search box. + return true; + } + if (keyCode == KeyEvent.KEYCODE_MENU) { + // Menu key goes to the overview mode similar to longpress, therefore it needs to + // dismiss the clings. + dismissLongPressCling(); + } + return false; + } + /** * Shows the migration cling. * @@ -92,6 +108,7 @@ class LauncherClings implements OnClickListener { * package was not preinstalled and there exists a db to migrate from. */ public void showMigrationCling() { + mLauncher.onLauncherClingShown(); mIsVisible = true; mLauncher.hideWorkspaceSearchAndHotseat(); @@ -136,7 +153,9 @@ class LauncherClings implements OnClickListener { final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content); mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content : R.layout.longpress_cling_content, content); - content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this); + final View button = content.findViewById(R.id.cling_dismiss_longpress_info); + button.setOnClickListener(this); + button.setOnKeyListener(this); if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) { Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg), @@ -144,6 +163,7 @@ class LauncherClings implements OnClickListener { content.setBackground(bg); } + mLauncher.onLauncherClingShown(); root.addView(cling); if (showWelcome) { @@ -161,12 +181,12 @@ class LauncherClings implements OnClickListener { ObjectAnimator anim; if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) { content.setTranslationY(-content.getMeasuredHeight()); - anim = LauncherAnimUtils.ofFloat(content, "translationY", 0); + anim = LauncherAnimUtils.ofFloat(content, View.TRANSLATION_Y, 0); } else { content.setScaleX(0); content.setScaleY(0); - PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1); - PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1); + PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1); + PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1); anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY); } @@ -180,7 +200,12 @@ class LauncherClings implements OnClickListener { @Thunk void dismissLongPressCling() { Runnable dismissCb = new Runnable() { public void run() { - dismissCling(mLauncher.findViewById(R.id.longpress_cling), null, + Runnable cb = new Runnable() { + public void run() { + mLauncher.onLauncherClingDismissed(); + } + }; + dismissCling(mLauncher.findViewById(R.id.longpress_cling), cb, WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION); } }; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index afd533016..0f043ce97 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -56,6 +56,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.model.MigrateFromRestoreTask; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; @@ -63,6 +64,7 @@ import com.android.launcher3.util.CursorIconInfo; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.ViewOnDrawExecutor; import java.lang.ref.WeakReference; import java.net.URISyntaxException; @@ -78,6 +80,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.Executor; /** * Maintains in-memory state of the Launcher. It is expected that there should be only one @@ -123,12 +126,6 @@ public class LauncherModel extends BroadcastReceiver @Thunk boolean mWorkspaceLoaded; @Thunk boolean mAllAppsLoaded; - // When we are loading pages synchronously, we can't just post the binding of items on the side - // pages as this delays the rotation process. Instead, we wait for a callback from the first - // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start - // a normal load, we also clear this set of Runnables. - static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>(); - /** * Set of runnables to be called on the background thread after the workspace binding * is complete. @@ -187,6 +184,7 @@ public class LauncherModel extends BroadcastReceiver public interface Callbacks { public boolean setLoadOnResume(); public int getCurrentWorkspaceScreen(); + public void clearPendingBinds(); public void startBinding(); public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, boolean forceAnimateIcons); @@ -211,6 +209,7 @@ public class LauncherModel extends BroadcastReceiver public void bindSearchProviderChanged(); public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); + public void executeOnNextDraw(ViewOnDrawExecutor executor); public void dumpLogsToLocalData(); } @@ -589,11 +588,6 @@ public class LauncherModel extends BroadcastReceiver "main thread"); } - // Clear any deferred bind runnables - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.clear(); - } - // Remove any queued UI runnables mHandler.cancelAll(); // Unbind all the workspace items @@ -652,12 +646,7 @@ public class LauncherModel extends BroadcastReceiver modelShortcut.cellX == shortcut.cellX && modelShortcut.cellY == shortcut.cellY && modelShortcut.spanX == shortcut.spanX && - modelShortcut.spanY == shortcut.spanY && - ((modelShortcut.dropPos == null && shortcut.dropPos == null) || - (modelShortcut.dropPos != null && - shortcut.dropPos != null && - modelShortcut.dropPos[0] == shortcut.dropPos[0] && - modelShortcut.dropPos[1] == shortcut.dropPos[1]))) { + modelShortcut.spanY == shortcut.spanY) { // For all intents and purposes, this is the same object return; } @@ -884,7 +873,7 @@ public class LauncherModel extends BroadcastReceiver } private void assertWorkspaceLoaded() { - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { synchronized (mLock) { if (!mHasLoaderCompletedOnce || (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) { @@ -1346,14 +1335,16 @@ public class LauncherModel extends BroadcastReceiver // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems InstallShortcutReceiver.enableInstallQueue(); synchronized (mLock) { - // Clear any deferred bind-runnables from the synchronized load process - // We must do this before any loading/binding is scheduled below. - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.clear(); - } - // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { + final Callbacks oldCallbacks = mCallbacks.get(); + // Clear any pending bind-runnables from the synchronized load process. + runOnMainThread(new Runnable() { + public void run() { + oldCallbacks.clearPendingBinds(); + } + }); + // If there is already one running, tell it to stop. stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags); @@ -1368,21 +1359,6 @@ public class LauncherModel extends BroadcastReceiver } } - void bindRemainingSynchronousPages() { - // Post the remaining side pages to be loaded - if (!mDeferredBindRunnables.isEmpty()) { - Runnable[] deferredBindRunnables = null; - synchronized (mDeferredBindRunnables) { - deferredBindRunnables = mDeferredBindRunnables.toArray( - new Runnable[mDeferredBindRunnables.size()]); - mDeferredBindRunnables.clear(); - } - for (final Runnable r : deferredBindRunnables) { - mHandler.post(r); - } - } - } - public void stopLoader() { synchronized (mLock) { if (mLoaderTask != null) { @@ -1725,8 +1701,7 @@ public class LauncherModel extends BroadcastReceiver final PackageManager manager = context.getPackageManager(); final boolean isSafeMode = manager.isSafeMode(); final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); - final boolean isSdCardReady = context.registerReceiver(null, - new IntentFilter(StartupReceiver.SYSTEM_READY)) != null; + final boolean isSdCardReady = Utilities.isBootCompleted(); LauncherAppState app = LauncherAppState.getInstance(); InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); @@ -2307,7 +2282,7 @@ public class LauncherModel extends BroadcastReceiver if (!isSdCardReady && !sPendingPackages.isEmpty()) { context.registerReceiver(new AppsAvailabilityCheck(), - new IntentFilter(StartupReceiver.SYSTEM_READY), + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, sWorker); } @@ -2457,19 +2432,36 @@ public class LauncherModel extends BroadcastReceiver private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) { final LauncherAppState app = LauncherAppState.getInstance(); final InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); - // XXX: review this + final int screenCols = profile.numColumns; + final int screenCellCount = profile.numColumns * profile.numRows; Collections.sort(workspaceItems, new Comparator<ItemInfo>() { @Override public int compare(ItemInfo lhs, ItemInfo rhs) { - int cellCountX = (int) profile.numColumns; - int cellCountY = (int) profile.numRows; - int screenOffset = cellCountX * cellCountY; - int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat - long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset + - lhs.cellY * cellCountX + lhs.cellX); - long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset + - rhs.cellY * cellCountX + rhs.cellX); - return (int) (lr - rr); + if (lhs.container == rhs.container) { + // Within containers, order by their spatial position in that container + switch ((int) lhs.container) { + case LauncherSettings.Favorites.CONTAINER_DESKTOP: { + long lr = (lhs.screenId * screenCellCount + + lhs.cellY * screenCols + lhs.cellX); + long rr = (rhs.screenId * screenCellCount + + rhs.cellY * screenCols + rhs.cellX); + return (int) (lr - rr); + } + case LauncherSettings.Favorites.CONTAINER_HOTSEAT: { + // We currently use the screen id as the rank + return (int) (lhs.screenId - rhs.screenId); + } + default: + if (ProviderConfig.IS_DOGFOOD_BUILD) { + throw new RuntimeException("Unexpected container type when " + + "sorting workspace items."); + } + return 0; + } + } else { + // Between containers, order by hotseat, desktop + return (int) (lhs.container - rhs.container); + } } }); } @@ -2492,9 +2484,7 @@ public class LauncherModel extends BroadcastReceiver final ArrayList<ItemInfo> workspaceItems, final ArrayList<LauncherAppWidgetInfo> appWidgets, final LongArrayMap<FolderInfo> folders, - ArrayList<Runnable> deferredBindRunnables) { - - final boolean postOnMainThread = (deferredBindRunnables != null); + final Executor executor) { // Bind the workspace items int N = workspaceItems.size(); @@ -2511,13 +2501,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - synchronized (deferredBindRunnables) { - deferredBindRunnables.add(r); - } - } else { - runOnMainThread(r); - } + executor.execute(r); } // Bind the folders @@ -2530,13 +2514,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - synchronized (deferredBindRunnables) { - deferredBindRunnables.add(r); - } - } else { - runOnMainThread(r); - } + executor.execute(r); } // Bind the widgets, one at a time @@ -2551,11 +2529,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - deferredBindRunnables.add(r); - } else { - runOnMainThread(r); - } + executor.execute(r); } } @@ -2633,6 +2607,7 @@ public class LauncherModel extends BroadcastReceiver public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { + callbacks.clearPendingBinds(); callbacks.startBinding(); } } @@ -2641,28 +2616,20 @@ public class LauncherModel extends BroadcastReceiver bindWorkspaceScreens(oldCallbacks, orderedScreenIds); - // Load items on the current page + Executor mainExecutor = new DeferredMainThreadExecutor(); + // Load items on the current page. bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, - currentFolders, null); - if (isLoadingSynchronously) { - r = new Runnable() { - public void run() { - Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) { - callbacks.onPageBoundSynchronously(currentScreen); - } - } - }; - runOnMainThread(r); - } + currentFolders, mainExecutor); - // Load all the remaining pages (if we are loading synchronously, we want to defer this - // work until after the first render) - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.clear(); - } - bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, - (isLoadingSynchronously ? mDeferredBindRunnables : null)); + // In case of isLoadingSynchronously, only bind the first screen, and defer binding the + // remaining screens after first onDraw is called. This ensures that the first screen + // is immediately visible (eg. during rotation) + // In case of !isLoadingSynchronously, bind all pages one after other. + final Executor deferredExecutor = isLoadingSynchronously ? + new ViewOnDrawExecutor(mHandler) : mainExecutor; + + bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, + otherFolders, deferredExecutor); // Tell the workspace that we're done binding items r = new Runnable() { @@ -2692,11 +2659,23 @@ public class LauncherModel extends BroadcastReceiver } }; + deferredExecutor.execute(r); + if (isLoadingSynchronously) { - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.add(r); - } - } else { + r = new Runnable() { + public void run() { + Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + // We are loading synchronously, which means, some of the pages will be + // bound after first draw. Inform the callbacks that page binding is + // not complete, and schedule the remaining pages. + if (currentScreen != PagedView.INVALID_RESTORE_PAGE) { + callbacks.onPageBoundSynchronously(currentScreen); + } + callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor); + } + } + }; runOnMainThread(r); } } @@ -3311,7 +3290,7 @@ public class LauncherModel extends BroadcastReceiver .setPackage(pkg), 0); needToRefresh |= widgets != null && !widgets.isEmpty(); } catch (RuntimeException e) { - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { throw e; } // Ignore the crash. We can live with a state widget list. @@ -3368,7 +3347,7 @@ public class LauncherModel extends BroadcastReceiver return results; } } catch (Exception e) { - if (!LauncherAppState.isDogfoodBuild() && + if (!ProviderConfig.IS_DOGFOOD_BUILD && (e.getCause() instanceof TransactionTooLargeException || e.getCause() instanceof DeadObjectException)) { // the returned value may be incomplete and will not be refreshed until the next @@ -3438,7 +3417,7 @@ public class LauncherModel extends BroadcastReceiver List<ResolveInfo> providers = packageManager.queryIntentActivities(shortcutsIntent, 0); sBgShortcutProviders = providers; } catch (RuntimeException e) { - if (!LauncherAppState.isDogfoodBuild() && + if (!ProviderConfig.IS_DOGFOOD_BUILD && (e.getCause() instanceof TransactionTooLargeException || e.getCause() instanceof DeadObjectException)) { /** @@ -3754,6 +3733,14 @@ public class LauncherModel extends BroadcastReceiver } } + @Thunk class DeferredMainThreadExecutor implements Executor { + + @Override + public void execute(Runnable command) { + runOnMainThread(command); + } + } + /** * @return the looper for the worker thread which can be used to start background tasks. */ diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 8791e9e57..c6827dae4 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -1113,10 +1113,6 @@ public class LauncherProvider extends ContentProvider { = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - final int uriIndex - = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); - final int displayModeIndex - = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); final int profileIndex = c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID); @@ -1235,9 +1231,6 @@ public class LauncherProvider extends ContentProvider { c.getString(iconResourceIndex)); values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType); values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1); - values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex)); - values.put(LauncherSettings.Favorites.DISPLAY_MODE, - c.getInt(displayModeIndex)); values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber); if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index cdde8c13f..d3af19a9e 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -23,15 +23,17 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.content.res.Resources; +import android.os.Build; import android.util.Log; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.allapps.AllAppsContainerView; -import com.android.launcher3.util.UiThreadCircularReveal; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.UiThreadCircularReveal; import com.android.launcher3.widget.WidgetsContainerView; import java.util.HashMap; @@ -162,7 +164,6 @@ public class LauncherStateTransitionAnimation { final boolean animated) { final WidgetsContainerView toView = mLauncher.getWidgetsView(); final View buttonView = mLauncher.getWidgetsButton(); - PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override public float getMaterialRevealViewFinalAlpha(View revealView) { @@ -175,11 +176,11 @@ public class LauncherStateTransitionAnimation { } /** - * Starts and animation to the workspace from the current overlay view. + * Starts an animation to the workspace from the current overlay view. */ public void startAnimationToWorkspace(final Launcher.State fromState, final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, - final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { + final boolean animated, final Runnable onCompleteRunnable) { if (toWorkspaceState != Workspace.State.NORMAL && toWorkspaceState != Workspace.State.SPRING_LOADED && toWorkspaceState != Workspace.State.OVERVIEW) { @@ -187,10 +188,14 @@ public class LauncherStateTransitionAnimation { } if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { - startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage, + startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, + animated, onCompleteRunnable); + } else if (fromState == Launcher.State.WIDGETS || + fromState == Launcher.State.WIDGETS_SPRING_LOADED) { + startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, animated, onCompleteRunnable); } else { - startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage, + startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState, animated, onCompleteRunnable); } } @@ -222,7 +227,7 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated - Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1, + Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, animated, layerViews); // Animate the search bar @@ -257,11 +262,11 @@ public class LauncherStateTransitionAnimation { // Create the animators PropertyValuesHolder panelAlpha = - PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f); + PropertyValuesHolder.ofFloat(View.ALPHA, revealViewToAlpha, 1f); PropertyValuesHolder panelDriftY = - PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0); + PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, revealViewToYDrift, 0); PropertyValuesHolder panelDriftX = - PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0); + PropertyValuesHolder.ofFloat(View.TRANSLATION_X, revealViewToXDrift, 0); ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, panelAlpha, panelDriftY, panelDriftX); panelAlphaAndDrift.setDuration(revealDuration); @@ -362,7 +367,7 @@ public class LauncherStateTransitionAnimation { if (layerViews.get(v) == BUILD_AND_SET_LAYER) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) { + if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) { v.buildLayer(); } } @@ -402,11 +407,11 @@ public class LauncherStateTransitionAnimation { } /** - * Starts and animation to the workspace from the apps view. + * Starts an animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final int toWorkspacePage, - final boolean animated, final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { AllAppsContainerView appsView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override @@ -441,17 +446,17 @@ public class LauncherStateTransitionAnimation { }; // Only animate the search bar if animating to spring loaded mode from all apps mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState, - toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(), + mLauncher.getAllAppsButton(), appsView, appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(), animated, onCompleteRunnable, cb); } /** - * Starts and animation to the workspace from the widgets view. + * Starts an animation to the workspace from the widgets view. */ private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final int toWorkspacePage, - final boolean animated, final Runnable onCompleteRunnable) { + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override @@ -470,16 +475,105 @@ public class LauncherStateTransitionAnimation { } }; mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, - toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView, + toWorkspaceState, mLauncher.getWidgetsButton(), widgetsView, widgetsView.getContentView(), widgetsView.getRevealView(), null, animated, onCompleteRunnable, cb); } /** + * Starts an animation to the workspace from another workspace state, e.g. normal to overview. + */ + private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + final View fromWorkspace = mLauncher.getWorkspace(); + final HashMap<View, Integer> layerViews = new HashMap<>(); + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); + final int revealDuration = mLauncher.getResources() + .getInteger(R.integer.config_overlayRevealTime); + + // Cancel the current animation + cancelAnimation(); + + // Create the workspace animation. + // NOTE: this call apparently also sets the state for the workspace if !animated + Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, + animated, layerViews); + + startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState, + animated ? revealDuration : 0, null); + + if (animated) { + if (workspaceAnim != null) { + animation.play(workspaceAnim); + } + dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, true); + + final AnimatorSet stateAnimation = animation; + final Runnable startAnimRunnable = new Runnable() { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void run() { + // Check that mCurrentAnimation hasn't changed while + // we waited for a layout/draw pass + if (mCurrentAnimation != stateAnimation) + return; + + dispatchOnLauncherTransitionStart(fromWorkspace, animated, true); + + // Enable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) { + v.buildLayer(); + } + } + stateAnimation.start(); + } + }; + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + // Disable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + + // This can hold unnecessary references to views. + cleanupAnimation(); + } + }); + fromWorkspace.post(startAnimRunnable); + mCurrentAnimation = animation; + } else /* if (!animated) */ { + dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, true); + dispatchOnLauncherTransitionStart(fromWorkspace, animated, true); + dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + mCurrentAnimation = null; + } + } + + /** * Creates and starts a new animation to the workspace. */ private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView, + final Workspace.State toWorkspaceState, final View buttonView, final View fromView, final View contentView, final View revealView, final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { @@ -503,7 +597,7 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, - toWorkspacePage, animated, layerViews); + animated, layerViews); // Animate the search bar startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState, @@ -657,6 +751,7 @@ public class LauncherStateTransitionAnimation { final AnimatorSet stateAnimation = animation; final Runnable startAnimRunnable = new Runnable() { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public void run() { // Check that mCurrentAnimation hasn't changed while // we waited for a layout/draw pass @@ -671,7 +766,7 @@ public class LauncherStateTransitionAnimation { if (layerViews.get(v) == BUILD_AND_SET_LAYER) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) { + if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) { v.buildLayer(); } } @@ -681,7 +776,7 @@ public class LauncherStateTransitionAnimation { fromView.post(startAnimRunnable); return animation; - } else { + } else /* if (!(animated && initialized)) */ { fromView.setVisibility(View.GONE); dispatchOnLauncherTransitionPrepare(fromView, animated, true); dispatchOnLauncherTransitionStart(fromView, animated, true); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 05f0a0553..e4d644884 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -18,10 +18,10 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; @@ -47,10 +47,8 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; - import com.android.launcher3.util.LauncherEdgeEffect; import com.android.launcher3.util.Thunk; - import java.util.ArrayList; /** @@ -65,9 +63,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // the min drag distance for a fling to register, to prevent random page shifts private static final int MIN_LENGTH_FOR_FLING = 25; - protected static final int PAGE_SNAP_ANIMATION_DURATION = 750; + public static final int PAGE_SNAP_ANIMATION_DURATION = 750; protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; - protected static final float NANOTIME_DIV = 1000000000.0f; private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; // The page is moved more than halfway, automatically move to the next page on touch up. @@ -87,25 +84,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private int mFreeScrollMinScrollX = -1; private int mFreeScrollMaxScrollX = -1; - static final int AUTOMATIC_PAGE_SPACING = -1; - protected int mFlingThresholdVelocity; protected int mMinFlingVelocity; protected int mMinSnapVelocity; - protected float mDensity; - protected float mSmoothingTime; - protected float mTouchX; - protected boolean mFirstLayout = true; private int mNormalChildHeight; protected int mCurrentPage; protected int mRestorePage = INVALID_RESTORE_PAGE; - protected int mChildCountOnLastLayout; + private int mChildCountOnLastLayout; protected int mNextPage = INVALID_PAGE; - protected int mMaxScrollX; + private int mMaxScrollX; protected LauncherScroller mScroller; private Interpolator mDefaultInterpolator; private VelocityTracker mVelocityTracker; @@ -117,10 +108,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private float mDownMotionY; private float mDownScrollX; private float mDragViewBaselineLeft; - protected float mLastMotionX; - protected float mLastMotionXRemainder; - protected float mLastMotionY; - protected float mTotalMotionX; + private float mLastMotionX; + private float mLastMotionXRemainder; + private float mLastMotionY; + private float mTotalMotionX; private int mLastScreenCenter = -1; private boolean mCancelTap; @@ -133,23 +124,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected final static int TOUCH_STATE_NEXT_PAGE = 3; protected final static int TOUCH_STATE_REORDERING = 4; - protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; - protected int mTouchState = TOUCH_STATE_REST; - protected boolean mForceScreenScrolled = false; + private boolean mForceScreenScrolled = false; protected OnLongClickListener mLongClickListener; protected int mTouchSlop; private int mMaximumVelocity; - protected int mPageLayoutWidthGap; - protected int mPageLayoutHeightGap; protected int mCellCountX = 0; protected int mCellCountY = 0; - protected boolean mCenterPagesVertically; protected boolean mAllowOverScroll = true; protected int[] mTempVisiblePagesRange = new int[2]; - protected boolean mForceDrawAllChildrenNextFrame; protected static final int INVALID_POINTER = -1; @@ -180,7 +165,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private float mMinScale = 1f; private boolean mUseMinScale = false; - protected View mDragView; + @Thunk View mDragView; private Runnable mSidePageHoverRunnable; @Thunk int mSidePageHoverIndex = -1; // This variable's scope is only for the duration of startReordering() and endReordering() @@ -189,7 +174,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // animation after endReordering() private boolean mIsReordering; // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition - private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2; + private static final int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2; private int mPostReorderingPreZoomInRemainingAnimationCount; private Runnable mPostReorderingPreZoomInRunnable; @@ -223,11 +208,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0); - - mPageLayoutWidthGap = a.getDimensionPixelSize( - R.styleable.PagedView_pageLayoutWidthGap, 0); - mPageLayoutHeightGap = a.getDimensionPixelSize( - R.styleable.PagedView_pageLayoutHeightGap, 0); mPageIndicatorViewId = a.getResourceId(R.styleable.PagedView_pageIndicator, -1); a.recycle(); @@ -243,16 +223,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mScroller = new LauncherScroller(getContext()); setDefaultInterpolator(new ScrollInterpolator()); mCurrentPage = 0; - mCenterPagesVertically = true; final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - mDensity = getResources().getDisplayMetrics().density; - mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); - mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity); - mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity); + float density = getResources().getDisplayMetrics().density; + mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density); + mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density); + mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density); setOnHierarchyChangeListener(this); setWillNotDraw(false); } @@ -393,7 +372,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } /** - * Returns the index of the currently displayed page. + * Returns the index of the currently displayed page. When in free scroll mode, this is the page + * that the user was on before entering free scroll mode (e.g. the home screen page they + * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()} + * to get the page the user is currently scrolling over. */ public int getCurrentPage() { return mCurrentPage; @@ -402,7 +384,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc /** * Returns the index of page to be shown immediately afterwards. */ - int getNextPage() { + public int getNextPage() { return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; } @@ -459,7 +441,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc Math.min(newPage, mTempVisiblePagesRange[1])); } // Ensure that it is clamped by the actual set of children in all cases - validatedPage = Math.max(0, Math.min(validatedPage, getPageCount() - 1)); + validatedPage = Utilities.boundInRange(validatedPage, 0, getPageCount() - 1); return validatedPage; } @@ -606,9 +588,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc super.scrollTo(x, y); } - mTouchX = x; - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - // Update the last motion events when scrolling if (isReordering(true)) { float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY); @@ -836,6 +815,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setMeasuredDimension(scaledWidthSize, scaledHeightSize); } + @SuppressLint("DrawAllocation") @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (getChildCount() == 0) { @@ -874,9 +854,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc childTop = offsetY; } else { childTop = offsetY + getPaddingTop() + mInsets.top; - if (mCenterPagesVertically) { - childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2; - } + childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2; } final int childWidth = child.getMeasuredWidth(); @@ -1064,11 +1042,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (pageCount > 0) { int viewportWidth = getViewportWidth(); - int curScreen = 0; + int lastVisiblePageIndex = 0; - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View currPage = getPageAt(i); + for (int currPageIndex = 0; currPageIndex < pageCount; currPageIndex++) { + View currPage = getPageAt(currPageIndex); sTmpIntPoint[0] = 0; Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false); @@ -1089,13 +1066,13 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc break; } } - curScreen = i; if (range[0] < 0) { - range[0] = curScreen; + range[0] = currPageIndex; } + lastVisiblePageIndex = currPageIndex; } - range[1] = curScreen; + range[1] = lastVisiblePageIndex; } else { range[0] = -1; range[1] = -1; @@ -1136,8 +1113,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc for (int i = pageCount - 1; i >= 0; i--) { final View v = getPageAt(i); if (v == mDragView) continue; - if (mForceDrawAllChildrenNextFrame || - (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) { + if (leftScreen <= i && i <= rightScreen && shouldDrawChild(v)) { drawChild(canvas, v, drawingTime); } } @@ -1146,7 +1122,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc drawChild(canvas, mDragView, drawingTime); } - mForceDrawAllChildrenNextFrame = false; canvas.restore(); } } @@ -1450,8 +1425,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mTotalMotionX += Math.abs(mLastMotionX - x); mLastMotionX = x; mLastMotionXRemainder = 0; - mTouchX = getViewportOffsetX() + getScrollX(); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; onScrollInteractionBegin(); pageBeginMoving(); } @@ -1650,8 +1623,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // keep the remainder because we are actually testing if we've moved from the last // scrolled position (which is discrete). if (Math.abs(deltaX) >= 1.0f) { - mTouchX += deltaX; - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; scrollBy((int) deltaX, 0); mLastMotionX = x; mLastMotionXRemainder = deltaX - (int) deltaX; @@ -1711,16 +1682,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Animate the view translation from its old position to its new // position - AnimatorSet anim = (AnimatorSet) v.getTag(ANIM_TAG_KEY); + ObjectAnimator anim = (ObjectAnimator) v.getTag(); if (anim != null) { anim.cancel(); } v.setTranslationX(oldX - newX); - anim = new AnimatorSet(); + anim = LauncherAnimUtils.ofFloat(v, View.TRANSLATION_X, 0); anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION); - anim.playTogether( - ObjectAnimator.ofFloat(v, "translationX", 0f)); anim.start(); v.setTag(anim); } @@ -2170,13 +2139,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Animate the drag view back to the original position private void animateDragViewToOriginalPosition() { if (mDragView != null) { - AnimatorSet anim = new AnimatorSet(); - anim.setDuration(REORDERING_DROP_REPOSITION_DURATION); - anim.playTogether( - ObjectAnimator.ofFloat(mDragView, "translationX", 0f), - ObjectAnimator.ofFloat(mDragView, "translationY", 0f), - ObjectAnimator.ofFloat(mDragView, "scaleX", 1f), - ObjectAnimator.ofFloat(mDragView, "scaleY", 1f)); + Animator anim = new LauncherViewPropertyAnimator(mDragView) + .translationX(0) + .translationY(0) + .scaleX(1) + .scaleY(1) + .setDuration(REORDERING_DROP_REPOSITION_DURATION); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -2273,9 +2241,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc animateDragViewToOriginalPosition(); } - private static final int ANIM_TAG_KEY = 100; - /* Accessibility */ + @SuppressWarnings("deprecation") @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { @@ -2335,7 +2302,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } protected String getCurrentPageDescription() { - return String.format(getContext().getString(R.string.default_scroll_format), + return getContext().getString(R.string.default_scroll_format, getNextPage() + 1, getChildCount()); } diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index 772a334b9..878a474e6 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -23,16 +23,15 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateInterpolator; -import android.widget.FrameLayout; +import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.util.Thunk; /* - * Ths bar will manage the transition between the QSB search bar and the delete drop - * targets so that each of the individual IconDropTargets don't have to. + * This bar will manage the transition between the QSB search bar and the delete/uninstall drop + * targets so that each of the individual ButtonDropTargets don't have to. */ -public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener { +public class SearchDropTargetBar extends BaseDropTargetBar { /** The different states that the search bar space can be in. */ public enum State { @@ -57,21 +56,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } } - private static int DEFAULT_DRAG_FADE_DURATION = 175; - private LauncherViewPropertyAnimator mDropTargetBarAnimator; private LauncherViewPropertyAnimator mQSBSearchBarAnimator; - private static final AccelerateInterpolator sAccelerateInterpolator = - new AccelerateInterpolator(); private State mState = State.SEARCH_BAR; @Thunk View mQSB; - @Thunk View mDropTargetBar; - private boolean mDeferOnDragEnd = false; - @Thunk boolean mAccessibilityEnabled = false; // Drop targets - private ButtonDropTarget mInfoDropTarget; private ButtonDropTarget mDeleteDropTarget; private ButtonDropTarget mUninstallDropTarget; @@ -83,61 +74,48 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D super(context, attrs, defStyle); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // Get the individual components + mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text); + mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar + .findViewById(R.id.uninstall_target_text); + + mDeleteDropTarget.setDropTargetBar(this); + mUninstallDropTarget.setDropTargetBar(this); + } + + @Override public void setup(Launcher launcher, DragController dragController) { dragController.addDragListener(this); dragController.setFlingToDeleteDropTarget(mDeleteDropTarget); - dragController.addDragListener(mInfoDropTarget); dragController.addDragListener(mDeleteDropTarget); dragController.addDragListener(mUninstallDropTarget); - dragController.addDropTarget(mInfoDropTarget); dragController.addDropTarget(mDeleteDropTarget); dragController.addDropTarget(mUninstallDropTarget); - mInfoDropTarget.setLauncher(launcher); mDeleteDropTarget.setLauncher(launcher); mUninstallDropTarget.setLauncher(launcher); } @Override - protected void onFinishInflate() { - super.onFinishInflate(); + public void showDropTarget() { + animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION); + } - // Get the individual components - mDropTargetBar = findViewById(R.id.drag_target_bar); - mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text); - mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text); - mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text); - - mInfoDropTarget.setSearchDropTargetBar(this); - mDeleteDropTarget.setSearchDropTargetBar(this); - mUninstallDropTarget.setSearchDropTargetBar(this); - - // Create the various fade animations - mDropTargetBar.setAlpha(0f); - mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar); - mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator); - mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - // Ensure that the view is visible for the animation - mDropTargetBar.setVisibility(View.VISIBLE); - } - - @Override - public void onAnimationEnd(Animator animation) { - if (mDropTargetBar != null) { - AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled); - } - } - }); + @Override + public void hideDropTarget() { + animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION); } public void setQsbSearchBar(View qsb) { mQSB = qsb; if (mQSB != null) { - // Update the search ber animation + // Update the search bar animation mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB); mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator); mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() { @@ -182,51 +160,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } /** - * Convenience method to animate the alpha of a view using hardware layers. - */ - private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha, - int duration) { - if (v == null) { - return; - } - - animator.cancel(); - if (Float.compare(v.getAlpha(), alpha) != 0) { - if (duration > 0) { - animator.alpha(alpha).withLayer().setDuration(duration).start(); - } else { - v.setAlpha(alpha); - AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled); - } - } - } - - /* - * DragController.DragListener implementation - */ - @Override - public void onDragStart(DragSource source, Object info, int dragAction) { - animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION); - } - - /** - * This is called to defer hiding the delete drop target until the drop animation has completed, - * instead of hiding immediately when the drag has ended. - */ - public void deferOnDragEnd() { - mDeferOnDragEnd = true; - } - - @Override - public void onDragEnd() { - if (!mDeferOnDragEnd) { - animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION); - } else { - mDeferOnDragEnd = false; - } - } - - /** * @return the bounds of the QSB search bar. */ public Rect getSearchBarBounds() { @@ -245,11 +178,11 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } } + @Override public void enableAccessibleDrag(boolean enable) { if (mQSB != null) { mQSB.setVisibility(enable ? View.GONE : View.VISIBLE); } - mInfoDropTarget.enableAccessibleDrag(enable); mDeleteDropTarget.enableAccessibleDrag(enable); mUninstallDropTarget.enableAccessibleDrag(enable); } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index 157b48a39..20c27735e 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -18,12 +18,9 @@ package com.android.launcher3; import android.app.WallpaperManager; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; public class ShortcutAndWidgetContainer extends ViewGroup { static final String TAG = "CellLayoutChildren"; @@ -43,7 +40,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup { private int mHeightGap; private int mCountX; - private int mCountY; private Launcher mLauncher; @@ -62,7 +58,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup { mWidthGap = widthGap; mHeightGap = heightGap; mCountX = countX; - mCountY = countY; } public View getChildAt(int x, int y) { @@ -80,24 +75,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup { } @Override - protected void dispatchDraw(Canvas canvas) { - @SuppressWarnings("all") // suppress dead code warning - final boolean debug = false; - if (debug) { - // Debug drawing for hit space - Paint p = new Paint(); - p.setColor(0x6600FF00); - for (int i = getChildCount() - 1; i >= 0; i--) { - final View child = getChildAt(i); - final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - - canvas.drawRect(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height, p); - } - } - super.dispatchDraw(canvas); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); @@ -238,7 +215,6 @@ public class ShortcutAndWidgetContainer extends ViewGroup { } } - @Override protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { super.setChildrenDrawnWithCacheEnabled(enabled); } diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 5766cf2f2..a86a2f9a5 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -29,7 +29,6 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import java.util.ArrayList; -import java.util.Arrays; /** * Represents a launchable icon on the workspaces and in folders. @@ -71,7 +70,7 @@ public class ShortcutInfo extends ItemInfo { /** * The intent used to start the application. */ - Intent intent; + public Intent intent; /** * Indicates whether the icon comes from an application's resource (if false) @@ -245,7 +244,7 @@ public class ShortcutInfo extends ItemInfo { return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY - + " dropPos=" + Arrays.toString(dropPos) + " user=" + user + ")"; + + " user=" + user + ")"; } public static void dumpShortcutInfoList(String tag, String label, diff --git a/src/com/android/launcher3/SimpleOnStylusPressListener.java b/src/com/android/launcher3/SimpleOnStylusPressListener.java new file mode 100644 index 000000000..6b97dcee6 --- /dev/null +++ b/src/com/android/launcher3/SimpleOnStylusPressListener.java @@ -0,0 +1,25 @@ +package com.android.launcher3; + +import android.view.MotionEvent; +import android.view.View; + +import com.android.launcher3.StylusEventHelper.StylusButtonListener; + +/** + * Simple listener that performs a long click on the view after a stylus button press. + */ +public class SimpleOnStylusPressListener implements StylusButtonListener { + private View mView; + + public SimpleOnStylusPressListener(View view) { + mView = view; + } + + public boolean onPressed(MotionEvent event) { + return mView.isLongClickable() && mView.performLongClick(); + } + + public boolean onReleased(MotionEvent event) { + return false; + } +}
\ No newline at end of file diff --git a/src/com/android/launcher3/StartupReceiver.java b/src/com/android/launcher3/StartupReceiver.java deleted file mode 100644 index 65f913fdf..000000000 --- a/src/com/android/launcher3/StartupReceiver.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.android.launcher3; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class StartupReceiver extends BroadcastReceiver { - - static final String SYSTEM_READY = "com.android.launcher3.SYSTEM_READY"; - - @Override - public void onReceive(Context context, Intent intent) { - context.sendStickyBroadcast(new Intent(SYSTEM_READY)); - } -} diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index cb0e252b2..4aba1503b 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -25,6 +25,8 @@ import android.util.Log; import android.view.View; import android.view.ViewParent; +import com.android.launcher3.config.ProviderConfig; + public class Stats { /** @@ -71,7 +73,7 @@ public class Stats { if (provider != null) { provider.fillInLaunchSourceData(sourceData); - } else if (LauncherAppState.isDogfoodBuild()) { + } else if (ProviderConfig.IS_DOGFOOD_BUILD) { throw new RuntimeException("Expected LaunchSourceProvider"); } } diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java index e03273a6e..d5fc0fad4 100644 --- a/src/com/android/launcher3/StylusEventHelper.java +++ b/src/com/android/launcher3/StylusEventHelper.java @@ -6,55 +6,82 @@ import android.view.ViewConfiguration; /** * Helper for identifying when a stylus touches a view while the primary stylus button is pressed. - * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. On a - * stylus button press this performs the view's {@link View#performLongClick()} method, if the view - * is long clickable. + * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. */ public class StylusEventHelper { - private boolean mIsButtonPressed; - private View mView; - public StylusEventHelper(View view) { - mView = view; + /** + * Implement this interface to receive callbacks for a stylus button press and release. + */ + public interface StylusButtonListener { + /** + * Called when the stylus button is pressed. + * + * @param event The MotionEvent that the button press occurred for. + * @return Whether the event was handled. + */ + public boolean onPressed(MotionEvent event); + + /** + * Called when the stylus button is released after a button press. This is also called if + * the event is canceled or the stylus is lifted off the screen. + * + * @param event The MotionEvent the button release occurred for. + * @return Whether the event was handled. + */ + public boolean onReleased(MotionEvent event); } + private boolean mIsButtonPressed; + private View mView; + private StylusButtonListener mListener; + private final float mSlop; + /** - * Call this in onTouchEvent method of a view to identify a stylus button press and perform a - * long click (if the view is long clickable). + * Constructs a helper for listening to stylus button presses and releases. Ensure that { + * {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on + * the helper to correctly identify stylus events. * - * @param event The event to check for a stylus button press. - * @return Whether a stylus event occurred and was handled. + * @param listener The listener to call for stylus events. + * @param view Optional view associated with the touch events. */ - public boolean checkAndPerformStylusEvent(MotionEvent event) { - final float slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop(); - - if (!mView.isLongClickable()) { - // We don't do anything unless the view is long clickable. - return false; + public StylusEventHelper(StylusButtonListener listener, View view) { + mListener = listener; + mView = view; + if (mView != null) { + mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop(); + } else { + mSlop = ViewConfiguration.getTouchSlop(); } + } + public boolean onMotionEvent(MotionEvent event) { final boolean stylusButtonPressed = isStylusButtonPressed(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - mIsButtonPressed = false; - if (stylusButtonPressed && mView.performLongClick()) { - mIsButtonPressed = true; - return true; + mIsButtonPressed = stylusButtonPressed; + if (mIsButtonPressed) { + return mListener.onPressed(event); } break; case MotionEvent.ACTION_MOVE: - if (Utilities.pointInView(mView, event.getX(), event.getY(), slop)) { - if (!mIsButtonPressed && stylusButtonPressed && mView.performLongClick()) { - mIsButtonPressed = true; - return true; - } else if (mIsButtonPressed && !stylusButtonPressed) { - mIsButtonPressed = false; - } + if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) { + return false; + } + if (!mIsButtonPressed && stylusButtonPressed) { + mIsButtonPressed = true; + return mListener.onPressed(event); + } else if (mIsButtonPressed && !stylusButtonPressed) { + mIsButtonPressed = false; + return mListener.onReleased(event); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mIsButtonPressed = false; + if (mIsButtonPressed) { + mIsButtonPressed = false; + return mListener.onReleased(event); + } break; } return false; diff --git a/src/com/android/launcher3/ToggleWeightWatcher.java b/src/com/android/launcher3/ToggleWeightWatcher.java deleted file mode 100644 index 33701a2c4..000000000 --- a/src/com/android/launcher3/ToggleWeightWatcher.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.android.launcher3; - -import android.app.Activity; - -public class ToggleWeightWatcher extends Activity { - -} diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 955d4013c..73881610b 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -8,7 +8,7 @@ import android.os.Bundle; import android.os.UserManager; import android.util.AttributeSet; import android.util.Pair; -import com.android.launcher3.R; + import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; @@ -32,7 +32,7 @@ public class UninstallDropTarget extends ButtonDropTarget { } @Override - protected boolean supportsDrop(DragSource source, Object info) { + protected boolean supportsDrop(DragSource source, ItemInfo info) { return supportsDrop(getContext(), info); } @@ -81,15 +81,18 @@ public class UninstallDropTarget extends ButtonDropTarget { @Override void completeDrop(final DragObject d) { final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo); - final UserHandleCompat user = ((ItemInfo) d.dragInfo).user; - if (startUninstallActivity(mLauncher, d.dragInfo)) { + final UserHandleCompat user = d.dragInfo.user; + if (startActivityWithUninstallAffordance(d)) { final Runnable checkIfUninstallWasSuccess = new Runnable() { @Override public void run() { - String packageName = componentInfo.first.getPackageName(); - boolean uninstallSuccessful = !AllAppsList.packageHasActivities( - getContext(), packageName, user); + boolean uninstallSuccessful = false; + if (componentInfo != null) { + String packageName = componentInfo.first.getPackageName(); + uninstallSuccessful = !AllAppsList.packageHasActivities( + getContext(), packageName, user); + } sendUninstallResult(d.dragSource, uninstallSuccessful); } }; @@ -99,9 +102,13 @@ public class UninstallDropTarget extends ButtonDropTarget { } } - public static boolean startUninstallActivity(Launcher launcher, Object info) { + protected boolean startActivityWithUninstallAffordance(DragObject d) { + return startUninstallActivity(mLauncher, d.dragInfo); + } + + public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) { final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info); - final UserHandleCompat user = ((ItemInfo) info).user; + final UserHandleCompat user = info.user; return launcher.startApplicationUninstallActivity( componentInfo.first, componentInfo.second, user); } @@ -115,7 +122,7 @@ public class UninstallDropTarget extends ButtonDropTarget { /** * Interface defining an object that can provide uninstallable drag objects. */ - public static interface UninstallSource { + public interface UninstallSource { /** * A pending uninstall operation was complete. diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index d5b28985c..31c141460 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -59,8 +59,11 @@ import android.util.TypedValue; import android.view.View; import android.widget.Toast; +import com.android.launcher3.config.ProviderConfig; + import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Locale; import java.util.Set; @@ -135,10 +138,9 @@ public final class Utilities { return Log.isLoggable(propertyName, Log.VERBOSE); } - public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) { + public static boolean isAllowRotationPrefEnabled(Context context) { SharedPreferences sharedPrefs = context.getSharedPreferences( - LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE | (multiProcess ? - Context.MODE_MULTI_PROCESS : 0)); + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); return sForceEnableRotation || allowRotationPref; } @@ -147,6 +149,18 @@ public final class Utilities { return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation); } + public static boolean isNycOrAbove() { + // TODO(vadimt): Replace using reflection with looking at the API version once + // Build.VERSION.SDK_INT gets bumped to 24. b/22942492. + try { + View.class.getDeclaredField("DRAG_FLAG_OPAQUE"); + // View.DRAG_FLAG_OPAQUE doesn't exist in M-release, so it's an indication of N+. + return true; + } catch (NoSuchFieldException e) { + return false; + } + } + public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { byte[] data = c.getBlob(iconIndex); try { @@ -359,15 +373,6 @@ public final class Utilities { localY < (v.getHeight() + slop); } - public static void scaleRect(Rect r, float scale) { - if (scale != 1.0f) { - r.left = (int) (r.left * scale + 0.5f); - r.top = (int) (r.top * scale + 0.5f); - r.right = (int) (r.right * scale + 0.5f); - r.bottom = (int) (r.bottom * scale + 0.5f); - } - } - public static int[] getCenterDeltaInScreenSpace(View v0, View v1, int[] delta) { v0.getLocationInWindow(sLoc0); v1.getLocationInWindow(sLoc1); @@ -388,11 +393,18 @@ public final class Utilities { } public static void scaleRectAboutCenter(Rect r, float scale) { - int cx = r.centerX(); - int cy = r.centerY(); - r.offset(-cx, -cy); - Utilities.scaleRect(r, scale); - r.offset(cx, cy); + if (scale != 1.0f) { + int cx = r.centerX(); + int cy = r.centerY(); + r.offset(-cx, -cy); + + r.left = (int) (r.left * scale + 0.5f); + r.top = (int) (r.top * scale + 0.5f); + r.right = (int) (r.right * scale + 0.5f); + r.bottom = (int) (r.bottom * scale + 0.5f); + + r.offset(cx, cy); + } } public static void startActivityForResultSafely( @@ -535,16 +547,6 @@ public final class Utilities { return null; } - @TargetApi(Build.VERSION_CODES.KITKAT) - public static boolean isViewAttachedToWindow(View v) { - if (ATLEAST_KITKAT) { - return v.isAttachedToWindow(); - } else { - // A proxy call which returns null, if the view is not attached to the window. - return v.getKeyDispatcherState() != null; - } - } - /** * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX} * provided by the same package which is set to be global search activity. @@ -680,7 +682,7 @@ public final class Utilities { } public static void assertWorkerThread() { - if (LauncherAppState.isDogfoodBuild() && + if (ProviderConfig.IS_DOGFOOD_BUILD && (LauncherModel.sWorkerThread.getThreadId() != Process.myTid())) { throw new IllegalStateException(); } @@ -730,6 +732,28 @@ public final class Utilities { return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values)); } + public static boolean isBootCompleted() { + try { + Class clazz = Class.forName("android.os.SystemProperties"); + Method getter = clazz.getDeclaredMethod("get", String.class); + String value = (String) getter.invoke(null, "sys.boot_completed"); + return "1".equals(value); + } catch (Exception e) { + Log.d(TAG, "Unable to read system properties"); + // Assume that boot has completed + return true; + } + } + + /** + * Ensures that a value is within given bounds. Specifically: + * If value is less than lowerBound, return lowerBound; else if value is greater than upperBound, + * return upperBound; else return value unchanged. + */ + public static int boundInRange(int value, int lowerBound, int upperBound) { + return Math.max(lowerBound, Math.min(value, upperBound)); + } + /** * Wraps a message with a TTS span, so that a different message is spoken than * what is getting displayed. diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java deleted file mode 100644 index 2d5612f12..000000000 --- a/src/com/android/launcher3/WallpaperChangedReceiver.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2013 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; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class WallpaperChangedReceiver extends BroadcastReceiver { - public void onReceive(Context context, Intent data) { - LauncherAppState.setApplicationContext(context.getApplicationContext()); - LauncherAppState appState = LauncherAppState.getInstance(); - appState.onWallpaperChanged(); - } -} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b63ddbaa7..5e052243c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -28,12 +28,10 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PointF; @@ -50,7 +48,6 @@ import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; -import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -69,6 +66,12 @@ import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.Accessi import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragScroller; +import com.android.launcher3.dragndrop.DragView; +import com.android.launcher3.dragndrop.SpringLoadedDragController; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; @@ -93,13 +96,17 @@ public class Workspace extends PagedView private static boolean ENFORCE_DRAG_EVENT_ORDER = false; - protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; - protected static final int FADE_EMPTY_SCREEN_DURATION = 150; + private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; + private static final int FADE_EMPTY_SCREEN_DURATION = 150; private static final int ADJACENT_SCREEN_DROP_DURATION = 300; - static final boolean MAP_NO_RECURSE = false; - static final boolean MAP_RECURSE = true; + private static final boolean MAP_NO_RECURSE = false; + private static final boolean MAP_RECURSE = true; + + // The screen id used for the empty screen always present to the right. + public final static long EXTRA_EMPTY_SCREEN_ID = -201; + private final static long CUSTOM_CONTENT_SCREEN_ID = -301; private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; private long mTouchDownTime = -1; @@ -114,10 +121,6 @@ public class Workspace extends PagedView private ShortcutAndWidgetContainer mDragSourceInternal; - // The screen id used for the empty screen always present to the right. - final static long EXTRA_EMPTY_SCREEN_ID = -201; - private final static long CUSTOM_CONTENT_SCREEN_ID = -301; - @Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>(); @Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>(); @@ -137,9 +140,6 @@ public class Workspace extends PagedView private int mDragOverX = -1; private int mDragOverY = -1; - static Rect mLandscapeCellLayoutMetrics = null; - static Rect mPortraitCellLayoutMetrics = null; - CustomContentCallbacks mCustomContentCallbacks; boolean mCustomContentShowing; private float mLastCustomContentScrollProgress = -1f; @@ -165,12 +165,11 @@ public class Workspace extends PagedView // These are temporary variables to prevent having to allocate a new object just to // return an (x, y) value from helper functions. Do NOT use them to maintain other state. - private int[] mTempCell = new int[2]; - private int[] mTempPt = new int[2]; - private int[] mTempEstimate = new int[2]; + private static final Rect sTempRect = new Rect(); + private final int[] mTempXY = new int[2]; @Thunk float[] mDragViewVisualCenter = new float[2]; private float[] mTempCellLayoutCenterCoordinates = new float[2]; - private Matrix mTempInverseMatrix = new Matrix(); + private int[] mTempVisiblePagesRange = new int[2]; private SpringLoadedDragController mSpringLoadedDragController; private float mSpringLoadedShrinkFactor; @@ -201,7 +200,6 @@ public class Workspace extends PagedView private boolean mIsSwitchingState = false; boolean mAnimatingViewIntoPlace = false; - boolean mIsDragOccuring = false; boolean mChildrenLayersEnabled = true; private boolean mStripScreensOnPageStopMoving = false; @@ -211,9 +209,6 @@ public class Workspace extends PagedView private HolographicOutlineHelper mOutlineHelper; @Thunk Bitmap mDragOutline = null; - private static final Rect sTempRect = new Rect(); - private final int[] mTempXY = new int[2]; - private int[] mTempVisiblePagesRange = new int[2]; public static final int DRAG_BITMAP_PADDING = 2; private boolean mWorkspaceFadeInAdjacentScreens; @@ -224,7 +219,6 @@ public class Workspace extends PagedView @Thunk Runnable mDelayedResizeRunnable; private Runnable mDelayedSnapToPageRunnable; - private Point mDisplaySize = new Point(); // Variables relating to the creation of user folders by hovering shortcuts over shortcuts private static final int FOLDER_CREATION_TIMEOUT = 0; @@ -278,19 +272,13 @@ public class Workspace extends PagedView boolean mStartedSendingScrollEvents; boolean mShouldSendPageSettled; int mLastOverlaySroll = 0; + private boolean mForceDrawAdjacentPages = false; // Handles workspace state transitions private WorkspaceStateTransitionAnimation mStateTransitionAnimation; private AccessibilityDelegate mPagesAccessibilityDelegate; - private final Runnable mBindPages = new Runnable() { - @Override - public void run() { - mLauncher.getModel().bindRemainingSynchronousPages(); - } - }; - /** * Used to inflate the Workspace from XML. * @@ -382,12 +370,11 @@ public class Workspace extends PagedView } @Override - public void onDragStart(final DragSource source, Object info, int dragAction) { + public void onDragStart(final DragSource source, ItemInfo info, int dragAction) { if (ENFORCE_DRAG_EVENT_ORDER) { enfoceDragParity("onDragStart", 0, 0); } - mIsDragOccuring = true; updateChildrenLayersEnabled(false); mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); @@ -418,7 +405,6 @@ public class Workspace extends PagedView removeExtraEmptyScreen(true, mDragSourceInternal != null); } - mIsDragOccuring = false; updateChildrenLayersEnabled(false); mLauncher.unlockScreenOrientation(false); @@ -446,8 +432,6 @@ public class Workspace extends PagedView setupLayoutTransition(); mWallpaperOffset = new WallpaperOffsetInterpolator(); - Display display = mLauncher.getWindowManager().getDefaultDisplay(); - display.getSize(mDisplaySize); mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); @@ -579,6 +563,7 @@ public class Workspace extends PagedView CellLayout customScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false); customScreen.disableDragTarget(); + customScreen.disableJailContent(); mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen); mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID); @@ -1319,12 +1304,12 @@ public class Workspace extends PagedView protected void setWallpaperDimension() { new AsyncTask<Void, Void, Void>() { public Void doInBackground(Void ... args) { - String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; - SharedPreferences sp = - mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); - WallpaperUtils.suggestWallpaperDimension(mLauncher.getResources(), - sp, mLauncher.getWindowManager(), mWallpaperManager, - mLauncher.overrideWallpaperDimensions()); + if (Utilities.ATLEAST_KITKAT) { + WallpaperUtils.suggestWallpaperDimension(mLauncher); + } else { + WallpaperUtils.suggestWallpaperDimensionPreK(mLauncher, + mLauncher.overrideWallpaperDimensions()); + } return null; } }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR); @@ -1598,6 +1583,7 @@ public class Workspace extends PagedView setOnClickListener(mLauncher); } mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable); + mLauncher.getAppInfoDropTargetBar().enableAccessibleDrag(enable); mLauncher.getHotseat().getLayout() .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); } @@ -1728,14 +1714,6 @@ public class Workspace extends PagedView } @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // Call back to LauncherModel to finish binding after the first draw - post(mBindPages); - } - - @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { if (!mLauncher.isAppsViewVisible()) { final Folder openFolder = getOpenFolder(); @@ -1861,8 +1839,18 @@ public class Workspace extends PagedView updateChildrenLayersEnabled(false); } + @Override + protected void getVisiblePages(int[] range) { + super.getVisiblePages(range); + if (mForceDrawAdjacentPages) { + // In overview mode, make sure that the two side pages are visible. + range[0] = Utilities.boundInRange(getCurrentPage() - 1, numCustomPages(), range[1]); + range[1] = Utilities.boundInRange(getCurrentPage() + 1, range[0], getPageCount() - 1); + } + } + protected void onWallpaperTap(MotionEvent ev) { - final int[] position = mTempCell; + final int[] position = mTempXY; getLocationOnScreen(position); int pointerIndex = ev.getActionIndex(); @@ -1926,6 +1914,18 @@ public class Workspace extends PagedView } public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) { + // Find a page that has enough space to place this widget (after rearranging/resizing). + // Start at the current page and search right (on LTR) until finding a page with enough + // space. Since an empty screen is the furthest right, a page must be found. + int currentPageInOverview = getPageNearestToCenterOfScreen(); + for (int pageIndex = currentPageInOverview; pageIndex < getPageCount(); pageIndex++) { + CellLayout page = (CellLayout) getPageAt(pageIndex); + if (page.hasReorderSolution(info)) { + setCurrentPage(pageIndex); + break; + } + } + int[] size = estimateItemSize(info, false); // The outline is used to visualize where the item will land if dropped @@ -1981,6 +1981,10 @@ public class Workspace extends PagedView return mState == State.OVERVIEW; } + public void snapToPageFromOverView(int whichPage) { + mStateTransitionAnimation.snapToPageFromOverView(whichPage); + } + int getOverviewModeTranslationY() { DeviceProfile grid = mLauncher.getDeviceProfile(); Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources())); @@ -1996,19 +2000,28 @@ public class Workspace extends PagedView return -workspaceOffsetTopEdge + overviewOffsetTopEdge; } + int getSpringLoadedTranslationY() { + return getOverviewModeTranslationY(); + } + /** * Sets the current workspace {@link State}, returning an animation transitioning the workspace * to that new state. */ - public Animator setStateWithAnimation(State toState, int toPage, boolean animated, + public Animator setStateWithAnimation(State toState, boolean animated, HashMap<View, Integer> layerViews) { // Create the animation to the new state Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState, - toState, toPage, animated, layerViews); + toState, animated, layerViews); // Update the current state mState = toState; updateAccessibilityFlags(); + if (mState == State.OVERVIEW || mState == State.SPRING_LOADED) { + // Redraw pages, as we might want to draw pages which were not visible. + mForceDrawAdjacentPages = true; + invalidate(); // This will call dispatchDraw(), which calls getVisiblePages(). + } return workspaceAnim; } @@ -2081,6 +2094,7 @@ public class Workspace extends PagedView mIsSwitchingState = false; updateChildrenLayersEnabled(false); showCustomContentIfNecessary(); + mForceDrawAdjacentPages = false; } void updateCustomContentVisibility() { @@ -2127,19 +2141,17 @@ public class Workspace extends PagedView * @param padding the horizontal and vertical padding to use when drawing */ private static void drawDragView(View v, Canvas destCanvas, int padding) { - final Rect clipRect = sTempRect; - v.getDrawingRect(clipRect); - - boolean textVisible = false; - destCanvas.save(); if (v instanceof TextView) { Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); - clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding); destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top); d.draw(destCanvas); } else { + final Rect clipRect = sTempRect; + v.getDrawingRect(clipRect); + + boolean textVisible = false; if (v instanceof FolderIcon) { // For FolderIcons the text can bleed into the icon area, and so we need to // hide the text completely (which can't be achieved by clipping). @@ -2317,7 +2329,8 @@ public class Workspace extends PagedView icon.clearPressedBackground(); } - if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) { + Object dragObject = child.getTag(); + if (!(dragObject instanceof ItemInfo)) { String msg = "Drag started with a view that has no tag set. This " + "will cause a crash (issue 11627249) down the line. " + "View: " + child + " tag: " + child.getTag(); @@ -2328,11 +2341,14 @@ public class Workspace extends PagedView mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } - DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), - DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible); + DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, + (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, + dragRect, scale, accessible); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); b.recycle(); + + mLauncher.enterSpringLoadedDragMode(); } public void beginExternalDragShared(View child, DragSource source) { @@ -2365,7 +2381,8 @@ public class Workspace extends PagedView Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2); Rect dragRect = new Rect(0, 0, iconSize, iconSize); - if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) { + Object dragObject = child.getTag(); + if (!(dragObject instanceof ItemInfo)) { String msg = "Drag started with a view that has no tag set. This " + "will cause a crash (issue 11627249) down the line. " + "View: " + child + " tag: " + child.getTag(); @@ -2373,12 +2390,15 @@ public class Workspace extends PagedView } // Start the drag - DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), - DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, false); + DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, + (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, + dragRect, scale, false); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); // Recycle temporary bitmaps tmpB.recycle(); + + mLauncher.enterSpringLoadedDragMode(); } public boolean transitionStateShouldAllowDrop() { @@ -2405,7 +2425,7 @@ public class Workspace extends PagedView if (mLauncher.isHotseatLayout(dropTargetLayout)) { mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { - mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); + mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter); } int spanX = 1; @@ -2415,9 +2435,8 @@ public class Workspace extends PagedView spanX = dragCellInfo.spanX; spanY = dragCellInfo.spanY; } else { - final ItemInfo dragInfo = (ItemInfo) d.dragInfo; - spanX = dragInfo.spanX; - spanY = dragInfo.spanY; + spanX = d.dragInfo.spanX; + spanY = d.dragInfo.spanY; } int minSpanX = spanX; @@ -2432,12 +2451,12 @@ public class Workspace extends PagedView mTargetCell); float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); - if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo, + if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo, dropTargetLayout, mTargetCell, distance, true)) { return true; } - if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo, + if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder(d.dragInfo, dropTargetLayout, mTargetCell, distance)) { return true; } @@ -2506,14 +2525,14 @@ public class Workspace extends PagedView return (aboveShortcut && willBecomeShortcut); } - boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell, + boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell, float distance) { if (distance > mMaxDistanceForFolderCreation) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willAddToExistingUserFolder(dragInfo, dropOverView); } - boolean willAddToExistingUserFolder(Object dragInfo, View dropOverView) { + boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) { if (dropOverView != null) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { @@ -2618,11 +2637,11 @@ public class Workspace extends PagedView if (mLauncher.isHotseatLayout(dropTargetLayout)) { mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { - mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); + mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter); } } - int snapScreen = WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE; + int snapScreen = -1; boolean resizeOnDrop = false; if (d.dragSource != this) { final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], @@ -2665,7 +2684,7 @@ public class Workspace extends PagedView // Aside from the special case where we're dropping a shortcut onto a shortcut, // we need to find the nearest cell location that is vacant - ItemInfo item = (ItemInfo) d.dragInfo; + ItemInfo item = d.dragInfo; int minSpanX = item.spanX; int minSpanY = item.spanY; if (item.minSpanX > 0 && item.minSpanY > 0) { @@ -2703,7 +2722,7 @@ public class Workspace extends PagedView CellLayout parentCell = getParentCellLayoutForView(cell); if (parentCell != null) { parentCell.removeView(cell); - } else if (LauncherAppState.isDogfoodBuild()) { + } else if (ProviderConfig.IS_DOGFOOD_BUILD) { throw new NullPointerException("mDragInfo.cell has null parent"); } addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1], @@ -2783,9 +2802,7 @@ public class Workspace extends PagedView animateWidgetDrop(info, parent, d.dragView, onCompleteRunnable, animationType, cell, false); } else { - int duration = snapScreen < 0 ? - WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE : - ADJACENT_SCREEN_DROP_DURATION; + int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, onCompleteRunnable, this); } @@ -2845,49 +2862,6 @@ public class Workspace extends PagedView CellLayout layout = getCurrentDropLayout(); setCurrentDropLayout(layout); setCurrentDragOverlappingLayout(layout); - - if (!workspaceInModalState()) { - mLauncher.getDragLayer().showPageHints(); - } - } - - /** Return a rect that has the cellWidth/cellHeight (left, top), and - * widthGap/heightGap (right, bottom) */ - static Rect getCellLayoutMetrics(Launcher launcher, int orientation) { - LauncherAppState app = LauncherAppState.getInstance(); - InvariantDeviceProfile inv = app.getInvariantDeviceProfile(); - - Display display = launcher.getWindowManager().getDefaultDisplay(); - Point smallestSize = new Point(); - Point largestSize = new Point(); - display.getCurrentSizeRange(smallestSize, largestSize); - int countX = (int) inv.numColumns; - int countY = (int) inv.numRows; - boolean isLayoutRtl = Utilities.isRtl(launcher.getResources()); - if (orientation == CellLayout.LANDSCAPE) { - if (mLandscapeCellLayoutMetrics == null) { - Rect padding = inv.landscapeProfile.getWorkspacePadding(isLayoutRtl); - int width = largestSize.x - padding.left - padding.right; - int height = smallestSize.y - padding.top - padding.bottom; - mLandscapeCellLayoutMetrics = new Rect(); - mLandscapeCellLayoutMetrics.set( - DeviceProfile.calculateCellWidth(width, countX), - DeviceProfile.calculateCellHeight(height, countY), 0, 0); - } - return mLandscapeCellLayoutMetrics; - } else if (orientation == CellLayout.PORTRAIT) { - if (mPortraitCellLayoutMetrics == null) { - Rect padding = inv.portraitProfile.getWorkspacePadding(isLayoutRtl); - int width = smallestSize.x - padding.left - padding.right; - int height = largestSize.y - padding.top - padding.bottom; - mPortraitCellLayoutMetrics = new Rect(); - mPortraitCellLayoutMetrics.set( - DeviceProfile.calculateCellWidth(width, countX), - DeviceProfile.calculateCellHeight(height, countY), 0, 0); - } - return mPortraitCellLayoutMetrics; - } - return null; } @Override @@ -2922,8 +2896,6 @@ public class Workspace extends PagedView setCurrentDragOverlappingLayout(null); mSpringLoadedDragController.cancel(); - - mLauncher.getDragLayer().hidePageHints(); } private void enfoceDragParity(String event, int update, int expectedValue) { @@ -3028,41 +3000,27 @@ public class Workspace extends PagedView * * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's * coordinate space. The argument xy is modified with the return result. - * - * if cachedInverseMatrix is not null, this method will just use that matrix instead of - * computing it itself; we use this to avoid redundant matrix inversions in - * findMatchingPageForDragOver - * */ - void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) { + void mapPointFromSelfToChild(View v, float[] xy) { xy[0] = xy[0] - v.getLeft(); xy[1] = xy[1] - v.getTop(); } - boolean isPointInSelfOverHotseat(int x, int y, Rect r) { - if (r == null) { - r = new Rect(); - } - mTempPt[0] = x; - mTempPt[1] = y; - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true); - - DeviceProfile grid = mLauncher.getDeviceProfile(); - r = grid.getHotseatRect(); - if (r.contains(mTempPt[0], mTempPt[1])) { - return true; - } - return false; + boolean isPointInSelfOverHotseat(int x, int y) { + mTempXY[0] = x; + mTempXY[1] = y; + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true); + return mLauncher.getDeviceProfile().isInHotseatRect(mTempXY[0], mTempXY[1]); } void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) { - mTempPt[0] = (int) xy[0]; - mTempPt[1] = (int) xy[1]; - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true); - mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt); + mTempXY[0] = (int) xy[0]; + mTempXY[1] = (int) xy[1]; + mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true); + mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempXY); - xy[0] = mTempPt[0]; - xy[1] = mTempPt[1]; + xy[0] = mTempXY[0]; + xy[1] = mTempXY[1]; } /* @@ -3108,10 +3066,7 @@ public class Workspace extends PagedView CellLayout cl = (CellLayout) getChildAt(i); final float[] touchXy = {originX, originY}; - // Transform the touch coordinates to the CellLayout's local coordinates - // If the touch point is within the bounds of the cell layout, we can return immediately - cl.getMatrix().invert(mTempInverseMatrix); - mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix); + mapPointFromSelfToChild(cl, touchXy); if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) { @@ -3153,11 +3108,10 @@ public class Workspace extends PagedView // Skip drag over events while we are dragging over side pages if (mInScrollArea || !transitionStateShouldAllowDrop()) return; - Rect r = new Rect(); CellLayout layout = null; - ItemInfo item = (ItemInfo) d.dragInfo; + ItemInfo item = d.dragInfo; if (item == null) { - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { throw new NullPointerException("DragObject has null info"); } return; @@ -3171,7 +3125,7 @@ public class Workspace extends PagedView // Identify whether we have dragged over a side page if (workspaceInModalState()) { if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) { - if (isPointInSelfOverHotseat(d.x, d.y, r)) { + if (isPointInSelfOverHotseat(d.x, d.y)) { layout = mLauncher.getHotseat().getLayout(); } } @@ -3194,7 +3148,7 @@ public class Workspace extends PagedView } else { // Test to see if we are over the hotseat otherwise just use the current page if (mLauncher.getHotseat() != null && !isDragWidget(d)) { - if (isPointInSelfOverHotseat(d.x, d.y, r)) { + if (isPointInSelfOverHotseat(d.x, d.y)) { layout = mLauncher.getHotseat().getLayout(); } } @@ -3213,7 +3167,7 @@ public class Workspace extends PagedView if (mLauncher.isHotseatLayout(mDragTargetLayout)) { mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { - mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null); + mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter); } int minSpanX = item.spanX; @@ -3275,7 +3229,7 @@ public class Workspace extends PagedView if (distance > mMaxDistanceForFolderCreation) return; final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]); - ItemInfo info = (ItemInfo) dragObject.dragInfo; + ItemInfo info = dragObject.dragInfo; boolean userFolderPending = willCreateUserFolder(info, dragOverView, false); if (mDragMode == DRAG_MODE_NONE && userFolderPending && !mFolderCreationAlarm.alarmPending()) { @@ -3397,24 +3351,6 @@ public class Workspace extends PagedView } /** - * Add the item specified by dragInfo to the given layout. - * @return true if successful - */ - public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) { - if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) { - onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false); - return true; - } - mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout)); - return false; - } - - private void onDropExternal(int[] touchXY, Object dragInfo, - CellLayout cellLayout, boolean insertAtFirst) { - onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null); - } - - /** * Drop an item that didn't originate on one of the workspace screens. * It may have come from Launcher (e.g. from all apps or customize), or it may have * come from another app altogether. @@ -3422,7 +3358,7 @@ public class Workspace extends PagedView * NOTE: This can also be called when we are outside of a drag event, when we want * to add an item to one of the workspace screens. */ - private void onDropExternal(final int[] touchXY, final Object dragInfo, + private void onDropExternal(final int[] touchXY, final ItemInfo dragInfo, final CellLayout cellLayout, boolean insertAtFirst, DragObject d) { final Runnable exitSpringLoadedRunnable = new Runnable() { @Override @@ -3432,7 +3368,7 @@ public class Workspace extends PagedView } }; - ItemInfo info = (ItemInfo) dragInfo; + ItemInfo info = dragInfo; int spanX = info.spanX; int spanY = info.spanY; if (mDragInfo != null) { @@ -3459,14 +3395,14 @@ public class Workspace extends PagedView cellLayout, mTargetCell); float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); - if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell, - distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo, - cellLayout, mTargetCell, distance)) { + if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true) + || willAddToExistingUserFolder( + d.dragInfo, cellLayout, mTargetCell, distance)) { findNearestVacantCell = false; } } - final ItemInfo item = (ItemInfo) d.dragInfo; + final ItemInfo item = d.dragInfo; boolean updateWidgetSize = false; if (findNearestVacantCell) { int minSpanX = item.spanX; @@ -3784,7 +3720,7 @@ public class Workspace extends PagedView mDragInfo.container, mDragInfo.screenId); if (cellLayout != null) { cellLayout.onDropChild(mDragInfo.cell); - } else if (LauncherAppState.isDogfoodBuild()) { + } else if (ProviderConfig.IS_DOGFOOD_BUILD) { throw new RuntimeException("Invalid state: cellLayout == null in " + "Workspace#onDropCompleted. Please file a bug. "); }; @@ -3795,6 +3731,9 @@ public class Workspace extends PagedView } mDragOutline = null; mDragInfo = null; + + mLauncher.exitSpringLoadedDragModeDelayed(success, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); } /** @@ -3804,7 +3743,7 @@ public class Workspace extends PagedView CellLayout parentCell = getParentCellLayoutForView(v); if (parentCell != null) { parentCell.removeView(v); - } else if (LauncherAppState.isDogfoodBuild()) { + } else if (ProviderConfig.IS_DOGFOOD_BUILD) { // When an app is uninstalled using the drop target, we wait until resume to remove // the icon. We also remove all the corresponding items from the workspace at // {@link Launcher#bindComponentsRemoved}. That call can come before or after @@ -3831,29 +3770,6 @@ public class Workspace extends PagedView } } - void updateItemLocationsInDatabase(CellLayout cl) { - int count = cl.getShortcutsAndWidgets().getChildCount(); - - long screenId = getIdForScreen(cl); - int container = Favorites.CONTAINER_DESKTOP; - - if (mLauncher.isHotseatLayout(cl)) { - screenId = -1; - container = Favorites.CONTAINER_HOTSEAT; - } - - for (int i = 0; i < count; i++) { - View v = cl.getShortcutsAndWidgets().getChildAt(i); - ItemInfo info = (ItemInfo) v.getTag(); - // Null check required as the AllApps button doesn't have an item info - if (info != null && info.requiresDbUpdate) { - info.requiresDbUpdate = false; - LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX, - info.cellY, info.spanX, info.spanY); - } - } - } - void saveWorkspaceToDb() { saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout()); int count = getChildCount(); @@ -3907,7 +3823,7 @@ public class Workspace extends PagedView @Override public boolean supportsAppInfoDropTarget() { - return false; + return true; } @Override @@ -4490,7 +4406,6 @@ public class Workspace extends PagedView } return String.format(getContext().getString(R.string.workspace_scroll_format), page + 1 - delta, nScreens); - } public void getLocationInDragLayer(int[] loc) { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 54f63bbd8..d32ce7377 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -30,6 +30,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.Thunk; import java.util.HashMap; @@ -174,7 +175,6 @@ public class WorkspaceStateTransitionAnimation { public static final String TAG = "WorkspaceStateTransitionAnimation"; - public static final int SCROLL_TO_CURRENT_PAGE = -1; @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350; final @Thunk Launcher mLauncher; @@ -217,14 +217,18 @@ public class WorkspaceStateTransitionAnimation { mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); } + public void snapToPageFromOverView(int whichPage) { + mWorkspace.snapToPage(whichPage, mOverviewTransitionTime, mZoomInInterpolator); + } + public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, - int toPage, boolean animated, HashMap<View, Integer> layerViews) { + boolean animated, HashMap<View, Integer> layerViews) { AccessibilityManager am = (AccessibilityManager) mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); final boolean accessibilityEnabled = am.isEnabled(); TransitionStates states = new TransitionStates(fromState, toState); int workspaceDuration = getAnimationDuration(states); - animateWorkspace(states, toPage, animated, workspaceDuration, layerViews, + animateWorkspace(states, animated, workspaceDuration, layerViews, accessibilityEnabled); animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION); return mStateAnimator; @@ -263,7 +267,7 @@ public class WorkspaceStateTransitionAnimation { /** * Starts a transition animation for the workspace. */ - private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated, + private void animateWorkspace(final TransitionStates states, final boolean animated, final int duration, final HashMap<View, Integer> layerViews, final boolean accessibilityEnabled) { // Reinitialize animation arrays for the current workspace state @@ -281,8 +285,13 @@ public class WorkspaceStateTransitionAnimation { float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ? 1f : 0f; float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f; - float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ? - mWorkspace.getOverviewModeTranslationY() : 0; + + float finalWorkspaceTranslationY = 0; + if (states.stateIsOverview || states.stateIsOverviewHidden) { + finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY(); + } else if (states.stateIsSpringLoaded) { + finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY(); + } final int childCount = mWorkspace.getChildCount(); final int customPageCount = mWorkspace.numCustomPages(); @@ -303,11 +312,7 @@ public class WorkspaceStateTransitionAnimation { } } - if (toPage == SCROLL_TO_CURRENT_PAGE) { - toPage = mWorkspace.getPageNearestToCenterOfScreen(); - } - mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator); - + int toPage = mWorkspace.getPageNearestToCenterOfScreen(); for (int i = 0; i < childCount; i++) { final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i); boolean isCurrentPage = (i == toPage); @@ -379,7 +384,6 @@ public class WorkspaceStateTransitionAnimation { mNewBackgroundAlphas[i] != 0) { ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha", mOldBackgroundAlphas[i], mNewBackgroundAlphas[i]); - LauncherAnimUtils.ofFloat(cl, 0f, 1f); bgAnim.setInterpolator(mZoomInInterpolator); bgAnim.setDuration(duration); mStateAnimator.play(bgAnim); @@ -488,8 +492,7 @@ public class WorkspaceStateTransitionAnimation { if (animated) { // These properties refer to the background protection gradient used for AllApps // and Widget tray. - ValueAnimator bgFadeOutAnimation = - LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha); + ValueAnimator bgFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha); bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 2233ebb7a..f8bf5a85b 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -20,7 +20,6 @@ import com.android.launcher3.AppInfo; import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; -import com.android.launcher3.DragController.DragListener; import com.android.launcher3.DragSource; import com.android.launcher3.Folder; import com.android.launcher3.FolderInfo; @@ -36,6 +35,7 @@ import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.UninstallDropTarget; import com.android.launcher3.Workspace; +import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -372,7 +372,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme @Override - public void onDragStart(DragSource source, Object info, int dragAction) { + public void onDragStart(DragSource source, ItemInfo info, int dragAction) { // No-op } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 564527e28..979a7332b 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -477,8 +477,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // Start the drag mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false); - // Enter spring loaded mode - mLauncher.enterSpringLoadedDragMode(); return false; } @@ -532,7 +530,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc int currentScreen = mLauncher.getCurrentWorkspaceScreen(); Workspace workspace = (Workspace) target; CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); - ItemInfo itemInfo = (ItemInfo) d.dragInfo; + ItemInfo itemInfo = d.dragInfo; if (layout != null) { showOutOfSpaceMessage = !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 1f95133d4..f885567ac 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -319,7 +319,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. } } - private Launcher mLauncher; + @Thunk Launcher mLauncher; private LayoutInflater mLayoutInflater; @Thunk AlphabeticalAppsList mApps; private GridLayoutManager mGridLayoutMgr; @@ -342,9 +342,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. // each time the search query changes. private String mMarketSearchMessage; // The intent to send off to the market app, updated each time the search query changes. - private Intent mMarketSearchIntent; + @Thunk Intent mMarketSearchIntent; // The last query that the user entered into the search field - private String mLastSearchQuery; + @Thunk String mLastSearchQuery; // Section drawing @Thunk int mSectionNamesMargin; @@ -414,11 +414,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. */ public void setLastSearchQuery(String query) { Resources res = mLauncher.getResources(); - String formatStr = res.getString(R.string.all_apps_no_search_results); mLastSearchQuery = query; - mEmptySearchMessage = String.format(formatStr, query); + mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query); if (mMarketAppName != null) { - mMarketSearchMessage = String.format(res.getString(R.string.all_apps_search_market_message), + mMarketSearchMessage = res.getString(R.string.all_apps_search_market_message, mMarketAppName); mMarketSearchIntent = createMarketSearchIntent(query); } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java index 14e2a1863..09a7d59bf 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java @@ -17,17 +17,15 @@ package com.android.launcher3.allapps; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.util.AttributeSet; -import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; + import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler; import com.android.launcher3.ClickShadowView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.R; /** * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java index 2b363c0cb..d853d5b8d 100644 --- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.allapps; -import android.content.ComponentName; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index dac0df12a..cb989e58b 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -16,13 +16,13 @@ package com.android.launcher3.allapps; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.util.Log; + import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.model.AppNameComparator; import com.android.launcher3.util.ComponentKey; @@ -186,7 +186,7 @@ public class AlphabeticalAppsList { // The of ordered component names as a result of a search query private ArrayList<ComponentKey> mSearchResults; private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>(); - private RecyclerView.Adapter mAdapter; + private AllAppsGridAdapter mAdapter; private AlphabeticIndexCompat mIndexer; private AppNameComparator mAppNameComparator; private MergeAlgorithm mMergeAlgorithm; @@ -215,7 +215,7 @@ public class AlphabeticalAppsList { /** * Sets the adapter to notify when this dataset changes. */ - public void setAdapter(RecyclerView.Adapter adapter) { + public void setAdapter(AllAppsGridAdapter adapter) { mAdapter = adapter; } @@ -419,7 +419,7 @@ public class AlphabeticalAppsList { if (info != null) { mPredictedApps.add(info); } else { - if (LauncherAppState.isDogfoodBuild()) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher)); } } diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java index ec1fb669f..463278ab4 100644 --- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -1,6 +1,7 @@ package com.android.launcher3.compat; import android.content.Context; + import com.android.launcher3.Utilities; import java.lang.reflect.Constructor; @@ -62,6 +63,7 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex { private boolean mHasValidAlphabeticIndex; private String mDefaultMiscLabel; + @SuppressWarnings({"unchecked", "rawtypes"}) public AlphabeticIndexCompat(Context context) { super(); try { diff --git a/src/com/android/launcher3/config/ProviderConfig.java b/src/com/android/launcher3/config/ProviderConfig.java index e8930d063..825b43422 100644 --- a/src/com/android/launcher3/config/ProviderConfig.java +++ b/src/com/android/launcher3/config/ProviderConfig.java @@ -19,4 +19,6 @@ package com.android.launcher3.config; public class ProviderConfig { public static final String AUTHORITY = "com.android.launcher3.settings".intern(); + + public static boolean IS_DOGFOOD_BUILD = false; } diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 204dddfdf..5097ea179 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.dragndrop; import android.content.ComponentName; import android.content.Context; @@ -26,15 +26,23 @@ import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.util.Log; +import android.view.DragEvent; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; -import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputMethodManager; +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.util.Thunk; @@ -44,7 +52,7 @@ import java.util.HashSet; /** * Class for initiating a drag within a view or across multiple views. */ -public class DragController { +public class DragController implements DragDriver.EventListener { private static final String TAG = "Launcher.DragController"; /** Indicates the drag is a move. */ @@ -61,9 +69,9 @@ public class DragController { private static final int SCROLL_OUTSIDE_ZONE = 0; private static final int SCROLL_WAITING_IN_ZONE = 1; - static final int SCROLL_NONE = -1; - static final int SCROLL_LEFT = 0; - static final int SCROLL_RIGHT = 1; + public static final int SCROLL_NONE = -1; + public static final int SCROLL_LEFT = 0; + public static final int SCROLL_RIGHT = 1; private static final float MAX_FLING_DEGREES = 35f; @@ -73,12 +81,14 @@ public class DragController { // temporaries to avoid gc thrash private Rect mRectTemp = new Rect(); private final int[] mCoordinatesTemp = new int[2]; - private final boolean mIsRtl; - /** Whether or not we're dragging. */ - private boolean mDragging; + /** + * Drag driver for the current drag/drop operation, or null if there is no active DND operation. + * It's null during accessible drag operations. + */ + private DragDriver mDragDriver = null; - /** Whether or not this is an accessible drag operation */ + /** Whether or not an accessible drag operation is in progress. */ private boolean mIsAccessibleDrag; /** X coordinate of the down event. */ @@ -87,11 +97,6 @@ public class DragController { /** Y coordinate of the down event. */ private int mMotionDownY; - /** the area at the edge of the screen that makes the workspace go left - * or right while you're dragging. - */ - private int mScrollZone; - private DropTarget.DragObject mDragObject; /** Who can receive drop events */ @@ -102,9 +107,6 @@ public class DragController { /** The window token used as the parent for the DragView. */ private IBinder mWindowToken; - /** The view that will be scrolled when dragging to the left and right edges of the screen. */ - private View mScrollView; - private View mMoveTarget; @Thunk DragScroller mDragScroller; @@ -117,12 +119,11 @@ public class DragController { @Thunk int mLastTouch[] = new int[2]; @Thunk long mLastTouchUpTime = -1; - @Thunk int mDistanceSinceScroll = 0; private int mTmpPoint[] = new int[2]; private Rect mDragLayerRect = new Rect(); - protected int mFlingToDeleteThresholdVelocity; + protected final int mFlingToDeleteThresholdVelocity; private VelocityTracker mVelocityTracker; /** @@ -137,14 +138,14 @@ public class DragController { * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE} * or {@link DragController#DRAG_ACTION_COPY} */ - void onDragStart(DragSource source, Object info, int dragAction); + void onDragStart(DragSource source, ItemInfo info, int dragAction); /** * The drag has ended */ void onDragEnd(); } - + /** * Used to create a new DragLayer from XML. */ @@ -152,17 +153,10 @@ public class DragController { Resources r = launcher.getResources(); mLauncher = launcher; mHandler = new Handler(); - mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone); mVelocityTracker = VelocityTracker.obtain(); - float density = r.getDisplayMetrics().density; mFlingToDeleteThresholdVelocity = - (int) (r.getInteger(R.integer.config_flingToDeleteMinVelocity) * density); - mIsRtl = Utilities.isRtl(r); - } - - public boolean dragging() { - return mDragging; + r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity); } /** @@ -178,7 +172,7 @@ public class DragController { * @param dragRegion Coordinates within the bitmap b for the position of item being dragged. * Makes dragging feel more precise, e.g. you can clip out a transparent border */ - public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, + public void startDrag(View v, Bitmap bmp, DragSource source, ItemInfo dragInfo, Rect viewImageBounds, int dragAction, float initialDragViewScale) { int[] loc = mCoordinatesTemp; mLauncher.getDragLayer().getLocationInDragLayer(v, loc); @@ -211,7 +205,7 @@ public class DragController { * @param accessible whether this drag should occur in accessibility mode */ public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY, - DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion, + DragSource source, ItemInfo dragInfo, int dragAction, Point dragOffset, Rect dragRegion, float initialDragViewScale, boolean accessible) { if (PROFILE_DRAWING_DURING_DRAG) { android.os.Debug.startMethodTracing("Launcher"); @@ -234,8 +228,8 @@ public class DragController { final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left; final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; - mDragging = true; mIsAccessibleDrag = accessible; + mLastDropTarget = null; mDragObject = new DropTarget.DragObject(); @@ -252,6 +246,8 @@ public class DragController { mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft); mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop); mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView); + + mDragDriver = DragDriver.create(this, dragInfo, dragView); } mDragObject.dragSource = source; @@ -319,18 +315,18 @@ public class DragController { * </pre> */ public boolean dispatchKeyEvent(KeyEvent event) { - return mDragging; + return mDragDriver != null; } public boolean isDragging() { - return mDragging; + return mDragDriver != null || mIsAccessibleDrag; } /** * Stop dragging without dropping. */ public void cancelDrag() { - if (mDragging) { + if (isDragging()) { if (mLastDropTarget != null) { mLastDropTarget.onDragExit(mDragObject); } @@ -341,6 +337,7 @@ public class DragController { } endDrag(); } + public void onAppsRemoved(final ArrayList<String> packageNames, HashSet<ComponentName> cns) { // Cancel the current drag if we are removing an app that we are dragging if (mDragObject != null) { @@ -364,8 +361,8 @@ public class DragController { } private void endDrag() { - if (mDragging) { - mDragging = false; + if (isDragging()) { + mDragDriver = null; mIsAccessibleDrag = false; clearScrollRunnable(); boolean isDeferred = false; @@ -416,18 +413,66 @@ public class DragController { return mTmpPoint; } - long getLastGestureUpTime() { - if (mDragging) { + public long getLastGestureUpTime() { + if (mDragDriver != null) { return System.currentTimeMillis(); } else { return mLastTouchUpTime; } } - void resetLastGestureUpTime() { + public void resetLastGestureUpTime() { mLastTouchUpTime = -1; } + @Override + public void onDriverDragMove(float x, float y) { + final int[] dragLayerPos = getClampedDragLayerPos(x, y); + + handleMoveEvent(dragLayerPos[0], dragLayerPos[1]); + } + + @Override + public void onDriverDragExitWindow() { + if (mLastDropTarget != null) { + mLastDropTarget.onDragExit(mDragObject); + mLastDropTarget = null; + } + } + + @Override + public void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride) { + final int[] dragLayerPos = getClampedDragLayerPos(x, y); + final int dragLayerX = dragLayerPos[0]; + final int dragLayerY = dragLayerPos[1]; + + DropTarget dropTarget; + PointF vec = null; + + if (dropTargetOverride != null) { + dropTarget = dropTargetOverride; + } else { + vec = isFlingingToDelete(mDragObject.dragSource); + if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) { + vec = null; + } + if (vec != null) { + dropTarget = mFlingToDeleteDropTarget; + } else { + dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp); + } + } + + drop(dropTarget, x, y, vec); + + endDrag(); + } + + @Override + public void onDriverDragCancel() { + cancelDrag(); + } + /** * Call this from a drag source view. */ @@ -435,8 +480,8 @@ public class DragController { @SuppressWarnings("all") // suppress dead code warning final boolean debug = false; if (debug) { - Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging=" - + mDragging); + Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " Dragging=" + + (mDragDriver != null)); } if (mIsAccessibleDrag) { @@ -452,41 +497,39 @@ public class DragController { final int dragLayerY = dragLayerPos[1]; switch (action) { - case MotionEvent.ACTION_MOVE: - break; case MotionEvent.ACTION_DOWN: // Remember location of down touch mMotionDownX = dragLayerX; mMotionDownY = dragLayerY; - mLastDropTarget = null; break; case MotionEvent.ACTION_UP: mLastTouchUpTime = System.currentTimeMillis(); - if (mDragging) { - PointF vec = isFlingingToDelete(mDragObject.dragSource); - if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) { - vec = null; - } - if (vec != null) { - dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec); - } else { - drop(dragLayerX, dragLayerY); - } - } - endDrag(); - break; - case MotionEvent.ACTION_CANCEL: - cancelDrag(); break; } - return mDragging; + return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev); + } + + /** + * Call this from a drag source view. + */ + public boolean onDragEvent(DragEvent event) { + return mDragDriver != null && mDragDriver.onDragEvent(event); + } + + /** + * Call this from a drag view. + */ + public void onDragViewAnimationEnd() { + if (mDragDriver != null) { + mDragDriver.onDragViewAnimationEnd(); + } } /** * Sets the view that should handle move events. */ - void setMoveTarget(View view) { + public void setMoveTarget(View view) { mMoveTarget = view; } @@ -500,7 +543,6 @@ public class DragController { mScrollState = SCROLL_OUTSIDE_ZONE; mScrollRunnable.setDirection(SCROLL_RIGHT); mDragScroller.onExitScrollArea(); - mLauncher.getDragLayer().onExitScrollArea(); } } @@ -514,11 +556,8 @@ public class DragController { mDragObject.y = coordinates[1]; checkTouchMove(dropTarget); - // Check if we are hovering over the scroll areas - mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y); mLastTouch[0] = x; mLastTouch[1] = y; - checkScrollState(x, y); } public void forceTouchMove() { @@ -546,41 +585,11 @@ public class DragController { mLastDropTarget = dropTarget; } - @Thunk void checkScrollState(int x, int y) { - final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop(); - final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY; - final DragLayer dragLayer = mLauncher.getDragLayer(); - final int forwardDirection = mIsRtl ? SCROLL_RIGHT : SCROLL_LEFT; - final int backwardsDirection = mIsRtl ? SCROLL_LEFT : SCROLL_RIGHT; - - if (x < mScrollZone) { - if (mScrollState == SCROLL_OUTSIDE_ZONE) { - mScrollState = SCROLL_WAITING_IN_ZONE; - if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) { - dragLayer.onEnterScrollArea(forwardDirection); - mScrollRunnable.setDirection(forwardDirection); - mHandler.postDelayed(mScrollRunnable, delay); - } - } - } else if (x > mScrollView.getWidth() - mScrollZone) { - if (mScrollState == SCROLL_OUTSIDE_ZONE) { - mScrollState = SCROLL_WAITING_IN_ZONE; - if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) { - dragLayer.onEnterScrollArea(backwardsDirection); - mScrollRunnable.setDirection(backwardsDirection); - mHandler.postDelayed(mScrollRunnable, delay); - } - } - } else { - clearScrollRunnable(); - } - } - /** * Call this from a drag source view. */ public boolean onTouchEvent(MotionEvent ev) { - if (!mDragging || mIsAccessibleDrag) { + if (mDragDriver == null || mIsAccessibleDrag) { return false; } @@ -593,47 +602,18 @@ public class DragController { final int dragLayerY = dragLayerPos[1]; switch (action) { - case MotionEvent.ACTION_DOWN: - // Remember where the motion event started - mMotionDownX = dragLayerX; - mMotionDownY = dragLayerY; - - if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) { - mScrollState = SCROLL_WAITING_IN_ZONE; - mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); - } else { - mScrollState = SCROLL_OUTSIDE_ZONE; - } - handleMoveEvent(dragLayerX, dragLayerY); - break; - case MotionEvent.ACTION_MOVE: - handleMoveEvent(dragLayerX, dragLayerY); - break; - case MotionEvent.ACTION_UP: - // Ensure that we've processed a move event at the current pointer location. - handleMoveEvent(dragLayerX, dragLayerY); - mHandler.removeCallbacks(mScrollRunnable); - - if (mDragging) { - PointF vec = isFlingingToDelete(mDragObject.dragSource); - if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) { - vec = null; - } - if (vec != null) { - dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec); - } else { - drop(dragLayerX, dragLayerY); - } - } - endDrag(); - break; - case MotionEvent.ACTION_CANCEL: - mHandler.removeCallbacks(mScrollRunnable); - cancelDrag(); - break; + case MotionEvent.ACTION_DOWN: + // Remember where the motion event started + mMotionDownX = dragLayerX; + mMotionDownY = dragLayerY; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mHandler.removeCallbacks(mScrollRunnable); + break; } - return true; + return mDragDriver.onTouchEvent(ev); } /** @@ -643,7 +623,6 @@ public class DragController { public void prepareAccessibleDrag(int x, int y) { mMotionDownX = x; mMotionDownY = y; - mLastDropTarget = null; } /** @@ -661,7 +640,7 @@ public class DragController { dropTarget.prepareAccessibilityDrop(); // Perform the drop - drop(location[0], location[1]); + drop(dropTarget, location[0], location[1], null); endDrag(); } @@ -676,64 +655,64 @@ public class DragController { ViewConfiguration config = ViewConfiguration.get(mLauncher); mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity()); - + PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + float theta = MAX_FLING_DEGREES + 1; if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) { // Do a quick dot product test to ensure that we are flinging upwards - PointF vel = new PointF(mVelocityTracker.getXVelocity(), - mVelocityTracker.getYVelocity()); PointF upVec = new PointF(0f, -1f); - float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) / - (vel.length() * upVec.length())); - if (theta <= Math.toRadians(MAX_FLING_DEGREES)) { - return vel; - } + theta = getAngleBetweenVectors(vel, upVec); + } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() && + mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) { + // Remove icon is on left side instead of top, so check if we are flinging to the left. + PointF leftVec = new PointF(-1f, 0f); + theta = getAngleBetweenVectors(vel, leftVec); + } + if (theta <= Math.toRadians(MAX_FLING_DEGREES)) { + return vel; } return null; } - private void dropOnFlingToDeleteTarget(float x, float y, PointF vel) { + private float getAngleBetweenVectors(PointF vec1, PointF vec2) { + return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) / + (vec1.length() * vec2.length())); + } + + void drop(DropTarget dropTarget, float x, float y, PointF flingVel) { final int[] coordinates = mCoordinatesTemp; mDragObject.x = coordinates[0]; mDragObject.y = coordinates[1]; - // Clean up dragging on the target if it's not the current fling delete target otherwise, - // start dragging to it. - if (mLastDropTarget != null && mFlingToDeleteDropTarget != mLastDropTarget) { - mLastDropTarget.onDragExit(mDragObject); + // Move dragging to the final target. + if (dropTarget != mLastDropTarget) { + if (mLastDropTarget != null) { + mLastDropTarget.onDragExit(mDragObject); + } + mLastDropTarget = dropTarget; + if (dropTarget != null) { + dropTarget.onDragEnter(mDragObject); + } } - // Drop onto the fling-to-delete target - boolean accepted = false; - mFlingToDeleteDropTarget.onDragEnter(mDragObject); - // We must set dragComplete to true _only_ after we "enter" the fling-to-delete target for - // "drop" mDragObject.dragComplete = true; - mFlingToDeleteDropTarget.onDragExit(mDragObject); - if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) { - mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, vel); - accepted = true; - } - mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject, true, - accepted); - } - private void drop(float x, float y) { - final int[] coordinates = mCoordinatesTemp; - final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates); - - mDragObject.x = coordinates[0]; - mDragObject.y = coordinates[1]; + // Drop onto the target. boolean accepted = false; if (dropTarget != null) { - mDragObject.dragComplete = true; dropTarget.onDragExit(mDragObject); if (dropTarget.acceptDrop(mDragObject)) { - dropTarget.onDrop(mDragObject); + if (flingVel != null) { + dropTarget.onFlingToDelete(mDragObject, flingVel); + } else { + dropTarget.onDrop(mDragObject); + } accepted = true; } } - mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, false, accepted); + final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null; + mDragObject.dragSource.onDropCompleted( + dropTargetAsView, mDragObject, flingVel != null, accepted); } private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) { @@ -819,17 +798,6 @@ public class DragController { } } - /** - * Set which view scrolls for touch events near the edge of the screen. - */ - public void setScrollView(View v) { - mScrollView = v; - } - - DragView getDragView() { - return mDragObject.dragView; - } - private class ScrollRunnable implements Runnable { private int mDirection; @@ -844,14 +812,7 @@ public class DragController { mDragScroller.scrollRight(); } mScrollState = SCROLL_OUTSIDE_ZONE; - mDistanceSinceScroll = 0; mDragScroller.onExitScrollArea(); - mLauncher.getDragLayer().onExitScrollArea(); - - if (isDragging()) { - // Check the scroll again so that we can requeue the scroller if necessary - checkScrollState(mLastTouch[0], mLastTouch[1]); - } } } diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java new file mode 100644 index 000000000..6e4b430d4 --- /dev/null +++ b/src/com/android/launcher3/dragndrop/DragDriver.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015 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.dragndrop; + +import com.android.launcher3.AnotherWindowDropTarget; +import com.android.launcher3.DropTarget; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Utilities; + +import android.content.ClipData; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Point; +import android.view.DragEvent; +import android.view.MotionEvent; +import android.view.View; + +/** + * Base class for driving a drag/drop operation. + */ +public abstract class DragDriver { + protected final EventListener mEventListener; + + public interface EventListener { + void onDriverDragMove(float x, float y); + void onDriverDragExitWindow(); + void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride); + void onDriverDragCancel(); + } + + public DragDriver(EventListener eventListener) { + mEventListener = eventListener; + } + + /** + * Handles ending of the DragView animation. + */ + public abstract void onDragViewAnimationEnd(); + + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + + switch (action) { + case MotionEvent.ACTION_MOVE: + mEventListener.onDriverDragMove(ev.getX(), ev.getY()); + break; + case MotionEvent.ACTION_UP: + mEventListener.onDriverDragMove(ev.getX(), ev.getY()); + mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null); + break; + case MotionEvent.ACTION_CANCEL: + mEventListener.onDriverDragCancel(); + break; + } + + return true; + } + + public abstract boolean onDragEvent (DragEvent event); + + + public boolean onInterceptTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + + switch (action) { + case MotionEvent.ACTION_UP: + mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null); + break; + case MotionEvent.ACTION_CANCEL: + mEventListener.onDriverDragCancel(); + break; + } + + return true; + } + + public static DragDriver create( + DragController dragController, ItemInfo dragInfo, DragView dragView) { + if (Utilities.isNycOrAbove()) { + return new SystemDragDriver(dragController, dragInfo.getIntent(), dragView); + } else { + return new InternalDragDriver(dragController); + } + } + +}; + +/** + * Class for driving a system (i.e. framework) drag/drop operation. + */ +class SystemDragDriver extends DragDriver { + /** Intent associated with the drag operation, or null is there no associated intent. */ + private final Intent mDragIntent; + + private final DragView mDragView; + boolean mIsFrameworkDragActive = false; + boolean mReceivedDropEvent = false; + float mLastX = 0; + float mLastY = 0; + + public SystemDragDriver(DragController dragController, Intent dragIntent, DragView dragView) { + super(dragController); + mDragIntent = dragIntent; + mDragView = dragView; + } + + private static class ShadowBuilder extends View.DragShadowBuilder { + final DragView mDragView; + + public ShadowBuilder(DragView dragView) { + mDragView = dragView; + } + + @Override + public void onProvideShadowMetrics (Point size, Point touch) { + mDragView.provideDragShadowMetrics(size, touch); + } + + @Override + public void onDrawShadow(Canvas canvas) { + mDragView.drawDragShadow(canvas); + } + }; + + @Override + public void onDragViewAnimationEnd() { + // Clip data for the drag operation. If there is an intent, create an intent-based ClipData, + // which will be passed to a global DND. + // If there is no intent, craft a fake ClipData and start a local DND operation; this + // ClipData will be ignored. + final ClipData dragData = mDragIntent != null ? + ClipData.newIntent("", mDragIntent) : + ClipData.newPlainText("", ""); + + View.DragShadowBuilder shadowBuilder = new ShadowBuilder(mDragView); + // TODO: DND flags are in flux, once settled, use the appropriate constant. + final int flagGlobal = 1 << 0; + final int flagOpaque = 1 << 9; + final int flags = (mDragIntent != null ? flagGlobal : 0) | flagOpaque; + + mIsFrameworkDragActive = true; + + if (!mDragView.startDrag(dragData, shadowBuilder, null, flags)) { + mIsFrameworkDragActive = false; + mEventListener.onDriverDragCancel(); + return; + } + + // Starting from this point, the driver takes over showing the drag shadow, so hiding the + // drag view. + mDragView.setVisibility(View.INVISIBLE); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return !mIsFrameworkDragActive && super.onTouchEvent(ev); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return !mIsFrameworkDragActive && super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onDragEvent (DragEvent event) { + if (!mIsFrameworkDragActive) { + // We are interested only in drag events started by this driver. + return false; + } + + final int action = event.getAction(); + + switch (action) { + case DragEvent.ACTION_DRAG_STARTED: + mLastX = event.getX(); + mLastY = event.getY(); + return true; + + case DragEvent.ACTION_DRAG_ENTERED: + mLastX = event.getX(); + mLastY = event.getY(); + return true; + + case DragEvent.ACTION_DRAG_LOCATION: + mLastX = event.getX(); + mLastY = event.getY(); + mEventListener.onDriverDragMove(event.getX(), event.getY()); + return true; + + case DragEvent.ACTION_DROP: + mLastX = event.getX(); + mLastY = event.getY(); + mReceivedDropEvent = true; + return true; + + case DragEvent.ACTION_DRAG_EXITED: + mLastX = event.getX(); + mLastY = event.getY(); + mEventListener.onDriverDragExitWindow(); + return true; + + case DragEvent.ACTION_DRAG_ENDED: + final boolean dragAccepted = event.getResult(); + final boolean acceptedByAnotherWindow = dragAccepted && !mReceivedDropEvent; + + // When the system drag ends, its drag shadow disappears. Resume showing the drag + // view for the possible final animation. + mDragView.setVisibility(View.VISIBLE); + + final DropTarget dropTargetOverride = acceptedByAnotherWindow ? + new AnotherWindowDropTarget(mDragView.getContext()) : null; + + mEventListener.onDriverDragEnd(mLastX, mLastY, dropTargetOverride); + mIsFrameworkDragActive = false; + return true; + + default: + return false; + } + } +}; + +/** + * Class for driving an internal (i.e. not using framework) drag/drop operation. + */ +class InternalDragDriver extends DragDriver { + public InternalDragDriver(DragController dragController) { + super(dragController); + } + + @Override + public void onDragViewAnimationEnd() {} + + @Override + public boolean onDragEvent (DragEvent event) { return false; } +}; diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 1c18747c1..adcc12cb6 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.dragndrop; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -28,6 +28,7 @@ import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.DragEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -39,9 +40,24 @@ import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.TextView; +import com.android.launcher3.AppWidgetResizeFrame; +import com.android.launcher3.CellLayout; +import com.android.launcher3.Folder; +import com.android.launcher3.FolderIcon; +import com.android.launcher3.InsettableFrameLayout; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetHostView; +import com.android.launcher3.SearchDropTargetBar; +import com.android.launcher3.ShortcutAndWidgetContainer; +import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.util.Thunk; +import com.android.launcher3.R; + import java.util.ArrayList; /** @@ -86,15 +102,6 @@ public class DragLayer extends InsettableFrameLayout { // Darkening scrim private float mBackgroundAlpha = 0; - // Related to adjacent page hints - private final Rect mScrollChildPosition = new Rect(); - private boolean mInScrollArea; - private boolean mShowPageHints; - private Drawable mLeftHoverDrawable; - private Drawable mRightHoverDrawable; - private Drawable mLeftHoverDrawableActive; - private Drawable mRightHoverDrawableActive; - private boolean mBlockTouches = false; /** @@ -110,12 +117,7 @@ public class DragLayer extends InsettableFrameLayout { setMotionEventSplittingEnabled(false); setChildrenDrawingOrderEnabled(true); - final Resources res = getResources(); - mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left); - mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right); - mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active); - mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active); - mIsRtl = Utilities.isRtl(res); + mIsRtl = Utilities.isRtl(getResources()); } public void setup(Launcher launcher, DragController controller) { @@ -163,6 +165,11 @@ public class DragLayer extends InsettableFrameLayout { if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { return true; } + + getDescendantRectRelativeToSelf(mLauncher.getAppInfoDropTargetBar(), mHitRect); + if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { + return true; + } return false; } @@ -322,6 +329,7 @@ public class DragLayer extends InsettableFrameLayout { if (isInAccessibleDrag()) { childrenForAccessibility.add(mLauncher.getSearchDropTargetBar()); + childrenForAccessibility.add(mLauncher.getAppInfoDropTargetBar()); } } else { super.addChildrenForAccessibility(childrenForAccessibility); @@ -375,6 +383,11 @@ public class DragLayer extends InsettableFrameLayout { return mDragController.onTouchEvent(ev); } + @Override + public boolean onDragEvent (DragEvent event) { + return mDragController.onDragEvent(event); + } + /** * Determine the rect of the descendant in this DragLayer's coordinates * @@ -764,7 +777,7 @@ public class DragLayer extends InsettableFrameLayout { // Show the drop view if it was previously hidden mDropView = view; mDropView.cancelAnimation(); - mDropView.resetLayoutParams(); + mDropView.requestLayout(); // Set the anchor view if the page is scrolling if (anchorView != null) { @@ -874,29 +887,6 @@ public class DragLayer extends InsettableFrameLayout { } } - void onEnterScrollArea(int direction) { - mInScrollArea = true; - invalidate(); - } - - void onExitScrollArea() { - mInScrollArea = false; - invalidate(); - } - - void showPageHints() { - mShowPageHints = true; - Workspace workspace = mLauncher.getWorkspace(); - getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()), - mScrollChildPosition); - invalidate(); - } - - void hidePageHints() { - mShowPageHints = false; - invalidate(); - } - @Override protected void dispatchDraw(Canvas canvas) { // Draw the background below children. @@ -908,41 +898,6 @@ public class DragLayer extends InsettableFrameLayout { super.dispatchDraw(canvas); } - private void drawPageHints(Canvas canvas) { - if (mShowPageHints) { - Workspace workspace = mLauncher.getWorkspace(); - int width = getMeasuredWidth(); - int page = workspace.getNextPage(); - CellLayout leftPage = (CellLayout) workspace.getChildAt(mIsRtl ? page + 1 : page - 1); - CellLayout rightPage = (CellLayout) workspace.getChildAt(mIsRtl ? page - 1 : page + 1); - - if (leftPage != null && leftPage.isDragTarget()) { - Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ? - mLeftHoverDrawableActive : mLeftHoverDrawable; - left.setBounds(0, mScrollChildPosition.top, - left.getIntrinsicWidth(), mScrollChildPosition.bottom); - left.draw(canvas); - } - if (rightPage != null && rightPage.isDragTarget()) { - Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ? - mRightHoverDrawableActive : mRightHoverDrawable; - right.setBounds(width - right.getIntrinsicWidth(), - mScrollChildPosition.top, width, mScrollChildPosition.bottom); - right.draw(canvas); - } - } - } - - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - boolean ret = super.drawChild(canvas, child, drawingTime); - - // We want to draw the page hints above the workspace, but below the drag view. - if (child instanceof Workspace) { - drawPageHints(canvas); - } - return ret; - } - public void setBackgroundAlpha(float alpha) { if (alpha != mBackgroundAlpha) { mBackgroundAlpha = alpha; diff --git a/src/com/android/launcher3/DragScroller.java b/src/com/android/launcher3/dragndrop/DragScroller.java index e261f15d8..165d0b11c 100644 --- a/src/com/android/launcher3/DragScroller.java +++ b/src/com/android/launcher3/dragndrop/DragScroller.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.dragndrop; /** * Handles scrolling while dragging diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 2acfc6140..88e11fa61 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -14,11 +14,14 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.dragndrop; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.FloatArrayEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.res.Resources; import android.graphics.Bitmap; @@ -33,8 +36,13 @@ import android.os.Build; import android.view.View; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.Utilities; import com.android.launcher3.util.Thunk; +import com.android.launcher3.R; + import java.util.Arrays; public class DragView extends View { @@ -45,19 +53,18 @@ public class DragView extends View { private Bitmap mBitmap; private Bitmap mCrossFadeBitmap; @Thunk Paint mPaint; - private int mRegistrationX; - private int mRegistrationY; + private final int mRegistrationX; + private final int mRegistrationY; private Point mDragVisualizeOffset = null; private Rect mDragRegion = null; - private DragLayer mDragLayer = null; + private final DragLayer mDragLayer; + @Thunk final DragController mDragController; private boolean mHasDrawn = false; @Thunk float mCrossFadeProgress = 0f; + private boolean mAnimationCancelled = false; ValueAnimator mAnim; - @Thunk float mOffsetX = 0.0f; - @Thunk float mOffsetY = 0.0f; - private float mInitialScale = 1f; // The intrinsic icon scale factor is the scale factor for a drag icon over the workspace // size. This is ignored for non-icons. private float mIntrinsicIconScale = 1f; @@ -81,7 +88,7 @@ public class DragView extends View { int left, int top, int width, int height, final float initialScale) { super(launcher); mDragLayer = launcher.getDragLayer(); - mInitialScale = initialScale; + mDragController = launcher.getDragController(); final Resources res = getResources(); final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); @@ -99,11 +106,6 @@ public class DragView extends View { public void onAnimationUpdate(ValueAnimator animation) { final float value = (Float) animation.getAnimatedValue(); - final int deltaX = (int) (-mOffsetX); - final int deltaY = (int) (-mOffsetY); - - mOffsetX += deltaX; - mOffsetY += deltaY; setScaleX(initialScale + (value * (scale - initialScale))); setScaleY(initialScale + (value * (scale - initialScale))); if (sDragAlpha != 1f) { @@ -112,9 +114,15 @@ public class DragView extends View { if (getParent() == null) { animation.cancel(); - } else { - setTranslationX(getTranslationX() + deltaX); - setTranslationY(getTranslationY() + deltaY); + } + } + }); + + mAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!mAnimationCancelled) { + mDragController.onDragViewAnimationEnd(); } } }); @@ -145,10 +153,6 @@ public class DragView extends View { return mIntrinsicIconScale; } - public float getOffsetY() { - return mOffsetY; - } - public int getDragRegionLeft() { return mDragRegion.left; } @@ -181,30 +185,33 @@ public class DragView extends View { return mDragRegion; } - public float getInitialScale() { - return mInitialScale; + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); } - public void updateInitialScaleToCurrentScale() { - mInitialScale = getScaleX(); + // Draws drag shadow for system DND. + @SuppressLint("WrongCall") + public void drawDragShadow(Canvas canvas) { + final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.scale(getScaleX(), getScaleY()); + onDraw(canvas); + canvas.restoreToCount(saveCount); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); + // Provides drag shadow metrics for system DND. + public void provideDragShadowMetrics(Point size, Point touch) { + size.set((int)(mBitmap.getWidth() * getScaleX()), (int)(mBitmap.getHeight() * getScaleY())); + + final float xGrowth = mBitmap.getWidth() * (getScaleX() - 1); + final float yGrowth = mBitmap.getHeight() * (getScaleY() - 1); + touch.set( + mRegistrationX + (int)Math.round(xGrowth / 2), + mRegistrationY + (int)Math.round(yGrowth / 2)); } @Override protected void onDraw(Canvas canvas) { - @SuppressWarnings("all") // suppress dead code warning - final boolean debug = false; - if (debug) { - Paint p = new Paint(); - p.setStyle(Paint.Style.FILL); - p.setColor(0x66ffffff); - canvas.drawRect(0, 0, getWidth(), getHeight(), p); - } - mHasDrawn = true; boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; if (crossFade) { @@ -214,12 +221,12 @@ public class DragView extends View { canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); if (crossFade) { mPaint.setAlpha((int) (255 * mCrossFadeProgress)); - canvas.save(); + final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); canvas.scale(sX, sY); canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); - canvas.restore(); + canvas.restoreToCount(saveCount); } } @@ -235,6 +242,7 @@ public class DragView extends View { @Override public void onAnimationUpdate(ValueAnimator animation) { mCrossFadeProgress = animation.getAnimatedFraction(); + invalidate(); } }); va.start(); @@ -328,16 +336,12 @@ public class DragView extends View { } public void cancelAnimation() { + mAnimationCancelled = true; if (mAnim != null && mAnim.isRunning()) { mAnim.cancel(); } } - public void resetLayoutParams() { - mOffsetX = mOffsetY = 0; - requestLayout(); - } - /** * Move the window containing this view. * @@ -345,8 +349,8 @@ public class DragView extends View { * @param touchY the y coordinate the user touched in DragLayer coordinates */ void move(int touchX, int touchY) { - setTranslationX(touchX - mRegistrationX + (int) mOffsetX); - setTranslationY(touchY - mRegistrationY + (int) mOffsetY); + setTranslationX(touchX - mRegistrationX); + setTranslationY(touchY - mRegistrationY); } void remove() { diff --git a/src/com/android/launcher3/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java index 45edaef86..d7f41c947 100644 --- a/src/com/android/launcher3/SpringLoadedDragController.java +++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java @@ -14,7 +14,13 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.dragndrop; + +import com.android.launcher3.Alarm; +import com.android.launcher3.CellLayout; +import com.android.launcher3.Launcher; +import com.android.launcher3.OnAlarmListener; +import com.android.launcher3.Workspace; public class SpringLoadedDragController implements OnAlarmListener { // how long the user must hover over a mini-screen before it unshrinks diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java index 30f228c68..ddc9cbfd9 100644 --- a/src/com/android/launcher3/model/PackageItemInfo.java +++ b/src/com/android/launcher3/model/PackageItemInfo.java @@ -20,8 +20,6 @@ import android.graphics.Bitmap; import com.android.launcher3.ItemInfo; -import java.util.Arrays; - /** * Represents a {@link Package} in the widget tray section. */ @@ -59,7 +57,6 @@ public class PackageItemInfo extends ItemInfo { return "PackageItemInfo(title=" + title + " id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY - + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) - + " user=" + user + ")"; + + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")"; } } diff --git a/src/com/android/launcher3/DummyWidget.java b/src/com/android/launcher3/testing/DummyWidget.java index 59cd80501..df887ac1f 100644 --- a/src/com/android/launcher3/DummyWidget.java +++ b/src/com/android/launcher3/testing/DummyWidget.java @@ -1,7 +1,10 @@ -package com.android.launcher3; +package com.android.launcher3.testing; import android.appwidget.AppWidgetProviderInfo; +import com.android.launcher3.CustomAppWidget; +import com.android.launcher3.R; + public class DummyWidget implements CustomAppWidget { @Override public String getLabel() { @@ -20,7 +23,7 @@ public class DummyWidget implements CustomAppWidget { @Override public int getWidgetLayout() { - return R.layout.dummy_widget; + return R.layout.zzz_dummy_widget; } @Override diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index dac279afa..c6d5ad18b 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -363,7 +363,8 @@ public class LauncherExtension extends Launcher { @Override public void onScrollInteractionEnd() { if (mProgress > 25 && mLauncherOverlayCallbacks.enterFullImmersion()) { - ObjectAnimator oa = LauncherAnimUtils.ofFloat(mSearchOverlay, "translationX", 0); + ObjectAnimator oa = LauncherAnimUtils.ofFloat( + mSearchOverlay, View.TRANSLATION_X, 0); oa.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator arg0) { diff --git a/src/com/android/launcher3/MemoryDumpActivity.java b/src/com/android/launcher3/testing/MemoryDumpActivity.java index d79be8007..9bcf92b1b 100644 --- a/src/com/android/launcher3/MemoryDumpActivity.java +++ b/src/com/android/launcher3/testing/MemoryDumpActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.testing; import android.app.Activity; import android.content.ComponentName; @@ -23,10 +23,20 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.net.Uri; -import android.os.*; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.IBinder; import android.util.Log; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.zip.ZipEntry; diff --git a/src/com/android/launcher3/MemoryTracker.java b/src/com/android/launcher3/testing/MemoryTracker.java index 067a50f97..ed2a3122c 100644 --- a/src/com/android/launcher3/MemoryTracker.java +++ b/src/com/android/launcher3/testing/MemoryTracker.java @@ -14,22 +14,28 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.testing; import android.app.ActivityManager; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.os.*; +import android.os.Binder; +import android.os.Debug; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.SystemClock; import android.util.Log; import android.util.LongSparseArray; +import com.android.launcher3.util.TestingUtils; + import java.util.ArrayList; import java.util.List; public class MemoryTracker extends Service { public static final String TAG = MemoryTracker.class.getSimpleName(); - public static final String ACTION_START_TRACKING = "com.android.launcher3.action.START_TRACKING"; private static final long UPDATE_RATE = 5000; @@ -83,14 +89,6 @@ public class MemoryTracker extends Service { ActivityManager mAm; - public static void startTrackingMe(Context context, String name) { - context.startService(new Intent(context, MemoryTracker.class) - .setAction(MemoryTracker.ACTION_START_TRACKING) - .putExtra("pid", android.os.Process.myPid()) - .putExtra("name", name) - ); - } - public ProcessMemInfo getMemInfo(int pid) { return mData.get(pid); } @@ -190,7 +188,7 @@ public class MemoryTracker extends Service { Log.v(TAG, "Received start id " + startId + ": " + intent); if (intent != null) { - if (ACTION_START_TRACKING.equals(intent.getAction())) { + if (TestingUtils.ACTION_START_TRACKING.equals(intent.getAction())) { final int pid = intent.getIntExtra("pid", -1); final String name = intent.getStringExtra("name"); final long start = intent.getLongExtra("start", System.currentTimeMillis()); diff --git a/src/com/android/launcher3/testing/ToggleWeightWatcher.java b/src/com/android/launcher3/testing/ToggleWeightWatcher.java new file mode 100644 index 000000000..15e55eea6 --- /dev/null +++ b/src/com/android/launcher3/testing/ToggleWeightWatcher.java @@ -0,0 +1,32 @@ +package com.android.launcher3.testing; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.util.TestingUtils; + +public class ToggleWeightWatcher extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); + boolean show = sp.getBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, true); + + show = !show; + sp.edit().putBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, show).apply(); + + Launcher launcher = (Launcher) LauncherAppState.getInstance().getModel().getCallback(); + if (launcher != null && launcher.mWeightWatcher != null) { + launcher.mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE); + } + finish(); + } +} diff --git a/src/com/android/launcher3/WeightWatcher.java b/src/com/android/launcher3/testing/WeightWatcher.java index 75684797f..a26a2b642 100644 --- a/src/com/android/launcher3/WeightWatcher.java +++ b/src/com/android/launcher3/testing/WeightWatcher.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.testing; import android.content.ComponentName; import android.content.Context; @@ -116,10 +116,6 @@ public class WeightWatcher extends LinearLayout { } } - public WeightWatcher(Context context) { - this(context, null); - } - @Override public void onAttachedToWindow() { super.onAttachedToWindow(); diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java index 55c5d7dc2..da8bae712 100644 --- a/src/com/android/launcher3/util/FlingAnimation.java +++ b/src/com/android/launcher3/util/FlingAnimation.java @@ -7,9 +7,9 @@ import android.graphics.PointF; import android.graphics.Rect; import android.view.animation.DecelerateInterpolator; -import com.android.launcher3.DragLayer; -import com.android.launcher3.DragView; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragView; public class FlingAnimation implements AnimatorUpdateListener { @@ -51,7 +51,7 @@ public class FlingAnimation implements AnimatorUpdateListener { mFrom.top += yOffset; mFrom.bottom -= yOffset; - mDuration = initDuration(); + mDuration = Math.abs(vel.y) > Math.abs(vel.x) ? initFlingUpDuration() : initFlingLeftDuration(); mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY); } @@ -62,7 +62,7 @@ public class FlingAnimation implements AnimatorUpdateListener { * - Calculate a constant acceleration in x direction such that the object reaches * {@link #mIconRect} in the given time. */ - protected int initDuration() { + protected int initFlingUpDuration() { float sY = -mFrom.bottom; float d = mUY * mUY + 2 * sY * MAX_ACCELERATION; @@ -83,6 +83,34 @@ public class FlingAnimation implements AnimatorUpdateListener { return (int) Math.round(t); } + /** + * The fling animation is based on the following system + * - Apply a constant force in the x direction to causing the fling to decelerate. + * - The animation runs for the time taken by the object to go out of the screen. + * - Calculate a constant acceleration in y direction such that the object reaches + * {@link #mIconRect} in the given time. + */ + protected int initFlingLeftDuration() { + float sX = -mFrom.right; + + float d = mUX * mUX + 2 * sX * MAX_ACCELERATION; + if (d >= 0) { + // sX can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for x direction. + mAX = MAX_ACCELERATION; + } else { + // sX is not reachable, decrease the acceleration so that sX is almost reached. + d = 0; + mAX = mUX * mUX / (2 * -sX); + } + double t = (-mUX - Math.sqrt(d)) / mAX; + + float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY(); + + // Find vertical acceleration such that: u*t + a*t*t/2 = s + mAY = (float) ((sY - t * mUY) * 2 / (t * t)); + return (int) Math.round(t); + } + public final int getDuration() { return mDuration + DRAG_END_DELAY; } diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index 696eabe00..1c6efbcf6 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -22,9 +22,6 @@ import android.view.View; import android.view.ViewGroup; import com.android.launcher3.CellLayout; -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.ShortcutAndWidgetContainer; import java.util.Arrays; diff --git a/src/com/android/launcher3/util/ParcelableSparseArray.java b/src/com/android/launcher3/util/ParcelableSparseArray.java new file mode 100644 index 000000000..093577e59 --- /dev/null +++ b/src/com/android/launcher3/util/ParcelableSparseArray.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2015 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.util; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +public class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable { + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + final int count = size(); + dest.writeInt(count); + for (int i = 0; i < count; i++) { + dest.writeInt(keyAt(i)); + dest.writeParcelable(valueAt(i), 0); + } + } + + public static final Parcelable.Creator<ParcelableSparseArray> CREATOR = + new Parcelable.Creator<ParcelableSparseArray>() { + public ParcelableSparseArray createFromParcel(Parcel source) { + final ParcelableSparseArray array = new ParcelableSparseArray(); + final ClassLoader loader = array.getClass().getClassLoader(); + final int count = source.readInt(); + for (int i = 0; i < count; i++) { + array.put(source.readInt(), source.readParcelable(loader)); + } + return array; + } + + public ParcelableSparseArray[] newArray(int size) { + return new ParcelableSparseArray[size]; + } + }; +} diff --git a/src/com/android/launcher3/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java new file mode 100644 index 000000000..39b80463d --- /dev/null +++ b/src/com/android/launcher3/util/TestingUtils.java @@ -0,0 +1,73 @@ +package com.android.launcher3.util; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.launcher3.CustomAppWidget; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; + +import java.util.HashMap; + +public class TestingUtils { + + public static final String MEMORY_TRACKER = "com.android.launcher3.testing.MemoryTracker"; + public static final String ACTION_START_TRACKING = "com.android.launcher3.action.START_TRACKING"; + + public static final boolean MEMORY_DUMP_ENABLED = false; + public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem"; + + public static final boolean ENABLE_CUSTOM_WIDGET_TEST = false; + public static final String DUMMY_WIDGET = "com.android.launcher3.testing.DummyWidget"; + + public static void startTrackingMemory(Context context) { + if (MEMORY_DUMP_ENABLED) { + context.startService(new Intent() + .setComponent(new ComponentName(context.getPackageName(), MEMORY_TRACKER)) + .setAction(ACTION_START_TRACKING) + .putExtra("pid", android.os.Process.myPid()) + .putExtra("name", "L")); + } + } + + public static void addWeightWatcher(Launcher launcher) { + if (MEMORY_DUMP_ENABLED) { + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = launcher.getSharedPreferences(spKey, Context.MODE_PRIVATE); + boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, true); + + int id = launcher.getResources().getIdentifier("zzz_weight_watcher", "layout", + launcher.getPackageName()); + View watcher = launcher.getLayoutInflater().inflate(id, null); + watcher.setAlpha(0.5f); + ((FrameLayout) launcher.findViewById(R.id.launcher)).addView(watcher, + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.BOTTOM) + ); + + watcher.setVisibility(show ? View.VISIBLE : View.GONE); + launcher.mWeightWatcher = watcher; + } + } + + public static void addDummyWidget(HashMap<String, CustomAppWidget> set) { + if (ENABLE_CUSTOM_WIDGET_TEST) { + try { + Class<?> clazz = Class.forName(DUMMY_WIDGET); + CustomAppWidget widget = (CustomAppWidget) clazz.newInstance(); + set.put(widget.getClass().getName(), widget); + } catch (Exception e) { + Log.e("TestingUtils", "Error adding dummy widget", e); + } + } + } +} diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java new file mode 100644 index 000000000..01808ba57 --- /dev/null +++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2015 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.util; + +import android.view.View; +import android.view.View.OnAttachStateChangeListener; +import android.view.ViewTreeObserver.OnDrawListener; + +import com.android.launcher3.DeferredHandler; +import com.android.launcher3.Launcher; + +import java.util.ArrayList; +import java.util.concurrent.Executor; + +/** + * An executor which runs all the tasks after the first onDraw is called on the target view. + */ +public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, + OnAttachStateChangeListener { + + private final ArrayList<Runnable> mTasks = new ArrayList<>(); + private final DeferredHandler mHandler; + + private Launcher mLauncher; + private View mAttachedView; + private boolean mCompleted; + + public ViewOnDrawExecutor(DeferredHandler handler) { + mHandler = handler; + } + + public void attachTo(Launcher launcher) { + mLauncher = launcher; + mAttachedView = launcher.getWorkspace(); + mAttachedView.addOnAttachStateChangeListener(this); + + attachObserver(); + } + + private void attachObserver() { + if (!mCompleted) { + mAttachedView.getViewTreeObserver().addOnDrawListener(this); + } + } + + @Override + public void execute(Runnable command) { + mTasks.add(command); + } + + @Override + public void onViewAttachedToWindow(View v) { + attachObserver(); + } + + @Override + public void onViewDetachedFromWindow(View v) { } + + @Override + public void onDraw() { + mAttachedView.post(this); + } + + @Override + public void run() { + for (final Runnable r : mTasks) { + mHandler.post(r); + } + markCompleted(); + } + + public void markCompleted() { + mTasks.clear(); + if (mAttachedView != null) { + mAttachedView.getViewTreeObserver().removeOnDrawListener(this); + mAttachedView.removeOnAttachStateChangeListener(this); + } + if (mLauncher != null) { + mLauncher.clearPendingExecutor(this); + } + } +} diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java index b9fccbcfd..a5251e190 100644 --- a/src/com/android/launcher3/util/WallpaperUtils.java +++ b/src/com/android/launcher3/util/WallpaperUtils.java @@ -17,13 +17,16 @@ package com.android.launcher3.util; import android.annotation.TargetApi; +import android.app.Activity; import android.app.WallpaperManager; +import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Point; import android.os.Build; import android.view.WindowManager; +import com.android.launcher3.LauncherFiles; import com.android.launcher3.Utilities; /** @@ -35,28 +38,59 @@ public final class WallpaperUtils { public static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; public static final float WALLPAPER_SCREENS_SPAN = 2f; - public static void suggestWallpaperDimension(Resources res, - final SharedPreferences sharedPrefs, - WindowManager windowManager, - final WallpaperManager wallpaperManager, boolean fallBackToDefaults) { - final Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(res, windowManager); - // If we have saved a wallpaper width/height, use that instead + public static void saveWallpaperDimensions(int width, int height, Activity activity) { + if (Utilities.ATLEAST_KITKAT) { + // From Kitkat onwards, ImageWallpaper does not care about the + // desired width and desired height of the wallpaper. + return; + } + String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; + SharedPreferences sp = activity.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); + SharedPreferences.Editor editor = sp.edit(); + if (width != 0 && height != 0) { + editor.putInt(WALLPAPER_WIDTH_KEY, width); + editor.putInt(WALLPAPER_HEIGHT_KEY, height); + } else { + editor.remove(WALLPAPER_WIDTH_KEY); + editor.remove(WALLPAPER_HEIGHT_KEY); + } + editor.commit(); + suggestWallpaperDimensionPreK(activity, true); + } + + public static void suggestWallpaperDimensionPreK( + Activity activity, boolean fallBackToDefaults) { + final Point defaultWallpaperSize = getDefaultWallpaperSize( + activity.getResources(), activity.getWindowManager()); - int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1); - int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1); + SharedPreferences sp = activity.getSharedPreferences( + LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY, Context.MODE_MULTI_PROCESS); + // If we have saved a wallpaper width/height, use that instead + int width = sp.getInt(WALLPAPER_WIDTH_KEY, -1); + int height = sp.getInt(WALLPAPER_HEIGHT_KEY, -1); - if (savedWidth == -1 || savedHeight == -1) { + if (width == -1 || height == -1) { if (!fallBackToDefaults) { return; } else { - savedWidth = defaultWallpaperSize.x; - savedHeight = defaultWallpaperSize.y; + width = defaultWallpaperSize.x; + height = defaultWallpaperSize.y; } } - if (savedWidth != wallpaperManager.getDesiredMinimumWidth() || - savedHeight != wallpaperManager.getDesiredMinimumHeight()) { - wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); + WallpaperManager wm = WallpaperManager.getInstance(activity); + if (width != wm.getDesiredMinimumWidth() || height != wm.getDesiredMinimumHeight()) { + wm.suggestDesiredDimensions(width, height); + } + } + + public static void suggestWallpaperDimension(Activity activity) { + // Only live wallpapers care about desired size. Update the size to what launcher expects. + final Point size = getDefaultWallpaperSize( + activity.getResources(), activity.getWindowManager()); + WallpaperManager wm = WallpaperManager.getInstance(activity); + if (size.x != wm.getDesiredMinimumWidth() || size.y != wm.getDesiredMinimumHeight()) { + wm.suggestDesiredDimensions(size.x, size.y); } } @@ -64,7 +98,7 @@ public final class WallpaperUtils { * As a ratio of screen height, the total distance we want the parallax effect to span * horizontally */ - public static float wallpaperTravelToScreenWidthRatio(int width, int height) { + private static float wallpaperTravelToScreenWidthRatio(int width, int height) { float aspectRatio = width / (float) height; // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width @@ -94,19 +128,10 @@ public final class WallpaperUtils { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { if (sDefaultWallpaperSize == null) { - Point minDims = new Point(); - Point maxDims = new Point(); - windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); - - int maxDim = Math.max(maxDims.x, maxDims.y); - int minDim = Math.max(minDims.x, minDims.y); - - if (Utilities.ATLEAST_JB_MR1) { - Point realSize = new Point(); - windowManager.getDefaultDisplay().getRealSize(realSize); - maxDim = Math.max(realSize.x, realSize.y); - minDim = Math.min(realSize.x, realSize.y); - } + Point realSize = new Point(); + windowManager.getDefaultDisplay().getRealSize(realSize); + int maxDim = Math.max(realSize.x, realSize.y); + int minDim = Math.min(realSize.x, realSize.y); // We need to ensure that there is enough extra space in the wallpaper // for the intended parallax effects diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 94bbd929f..70eceb9ce 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -36,6 +36,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.SimpleOnStylusPressListener; import com.android.launcher3.R; import com.android.launcher3.StylusEventHelper; import com.android.launcher3.WidgetPreviewLoader; @@ -93,7 +94,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { final Resources r = context.getResources(); mLauncher = (Launcher) context; - mStylusEventHelper = new StylusEventHelper(this); + mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); mDimensionsFormatString = r.getString(R.string.widget_dims_format); setContainerWidth(); @@ -211,7 +212,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { @Override public boolean onTouchEvent(MotionEvent ev) { boolean handled = super.onTouchEvent(ev); - if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + if (mStylusEventHelper.onMotionEvent(ev)) { return true; } return handled; diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 461aebb6b..7607d8554 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -10,13 +10,14 @@ import android.os.Handler; import android.view.View; import com.android.launcher3.AppWidgetResizeFrame; -import com.android.launcher3.DragController.DragListener; -import com.android.launcher3.DragLayer; import com.android.launcher3.DragSource; +import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.dragndrop.DragController.DragListener; +import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.Thunk; public class WidgetHostViewLoader implements DragListener { @@ -44,7 +45,7 @@ public class WidgetHostViewLoader implements DragListener { } @Override - public void onDragStart(DragSource source, Object info, int dragAction) { } + public void onDragStart(DragSource source, ItemInfo info, int dragAction) { } @Override public void onDragEnd() { diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 64d33aa09..0f5ca15b6 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -32,7 +32,6 @@ import com.android.launcher3.BaseContainerView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.DragController; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Folder; @@ -45,6 +44,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.Workspace; +import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.Thunk; @@ -90,7 +90,7 @@ public class WidgetsContainerView extends BaseContainerView super(context, attrs, defStyleAttr); mLauncher = (Launcher) context; mDragController = mLauncher.getDragController(); - mAdapter = new WidgetsListAdapter(context, this, this, mLauncher); + mAdapter = new WidgetsListAdapter(this, this, mLauncher); mIconCache = (LauncherAppState.getInstance()).getIconCache(); if (DEBUG) { Log.d(TAG, "WidgetsContainerView constructor"); @@ -255,9 +255,11 @@ public class WidgetsContainerView extends BaseContainerView // Start the drag mLauncher.lockScreenOrientation(); - mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha); mDragController.startDrag(image, preview, this, createItemInfo, bounds, DragController.DRAG_ACTION_COPY, scale); + // This call expects the extra empty screen to already be created, which is why we call it + // after mDragController.startDrag(). + mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha); preview.recycle(); return true; @@ -319,7 +321,7 @@ public class WidgetsContainerView extends BaseContainerView int currentScreen = mLauncher.getCurrentWorkspaceScreen(); Workspace workspace = (Workspace) target; CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); - ItemInfo itemInfo = (ItemInfo) d.dragInfo; + ItemInfo itemInfo = d.dragInfo; if (layout != null) { showOutOfSpaceMessage = !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index f1cde299d..ac9d62e9a 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -27,7 +27,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; -import android.view.ViewGroup.MarginLayoutParams; import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; @@ -65,20 +64,17 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { private View.OnClickListener mIconClickListener; private View.OnLongClickListener mIconLongClickListener; - private static final int PRESET_INDENT_SIZE_TABLET = 56; - private int mIndent = 0; + private final int mIndent; - public WidgetsListAdapter(Context context, - View.OnClickListener iconClickListener, + public WidgetsListAdapter(View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener, Launcher launcher) { - mLayoutInflater = LayoutInflater.from(context); + mLayoutInflater = launcher.getLayoutInflater(); mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; mLauncher = launcher; - - setContainerHeight(); + mIndent = launcher.getResources().getDimensionPixelSize(R.dimen.widget_section_indent); } public void setWidgetsModel(WidgetsModel w) { @@ -207,12 +203,4 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { } return mWidgetPreviewLoader; } - - private void setContainerHeight() { - Resources r = mLauncher.getResources(); - DeviceProfile profile = mLauncher.getDeviceProfile(); - if (profile.isLargeTablet || profile.isTablet) { - mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); - } - } } diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java index 249559ab9..19bc868a4 100644 --- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java +++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java @@ -16,10 +16,7 @@ package com.android.launcher3.widget; import android.support.v7.widget.RecyclerView.ViewHolder; -import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; public class WidgetsRowViewHolder extends ViewHolder { |