Merge "Adding a utility class to enforce that icons are always flattened on the background thread." into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index ad8ce02..28beff2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -35,7 +35,7 @@
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res \
- frameworks/support/v7/recyclerview/res
+ prebuilts/sdk/current/support/v7/recyclerview/res \
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -48,6 +48,7 @@
LOCAL_SDK_VERSION := current
LOCAL_MIN_SDK_VERSION := 21
LOCAL_PACKAGE_NAME := Launcher3
+LOCAL_PRIVILEGED_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2
LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml
index 708ece4..d6bd0c5 100644
--- a/res/layout/appwidget_error.xml
+++ b/res/layout/appwidget_error.xml
@@ -19,6 +19,7 @@
android:layout_height="match_parent"
android:gravity="center"
android:elevation="2dp"
+ android:theme="@style/WidgetContainerTheme"
android:background="@drawable/quantum_panel_dark"
android:textAppearance="?android:attr/textAppearanceMediumInverse"
android:textColor="@color/widgets_view_item_text_color"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 3423835..32bccb8 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -27,6 +27,7 @@
<enum name="all_apps" value="1" />
<enum name="folder" value="2" />
<enum name="widget_section" value="3" />
+ <enum name="shortcut_popup" value="4" />
</attr>
<attr name="deferShadowGeneration" format="boolean" />
<attr name="customShadows" format="boolean" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index bb0bc2f..4e70f43 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -108,6 +108,7 @@
<item name="android:shadowRadius">0</item>
<item name="customShadows">false</item>
<item name="layoutHorizontal">true</item>
+ <item name="iconDisplay">shortcut_popup</item>
<item name="iconSizeOverride">@dimen/deep_shortcut_icon_size</item>
</style>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index d00d5dd..0380923 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -223,8 +223,8 @@
* Based on the current deltas, we determine if and how to resize the widget.
*/
private void resizeWidgetIfNeeded(boolean onDismiss) {
- float xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
- float yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
+ float xThreshold = mCellLayout.getCellWidth();
+ float yThreshold = mCellLayout.getCellHeight();
int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
@@ -337,8 +337,8 @@
}
private void onTouchUp() {
- int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
- int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
+ int xThreshold = mCellLayout.getCellWidth();
+ int yThreshold = mCellLayout.getCellHeight();
mDeltaXAddOn = mRunningHInc * xThreshold;
mDeltaYAddOn = mRunningVInc * yThreshold;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 979c950..3564cec 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -87,13 +87,6 @@
@ViewDebug.ExportedProperty(category = "launcher")
private int mCountY;
- private int mOriginalWidthGap;
- private int mOriginalHeightGap;
- @ViewDebug.ExportedProperty(category = "launcher")
- @Thunk int mWidthGap;
- @ViewDebug.ExportedProperty(category = "launcher")
- @Thunk int mHeightGap;
- private int mMaxGap;
private boolean mDropPending = false;
private boolean mIsDragTarget = true;
private boolean mJailContent = true;
@@ -202,9 +195,6 @@
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
- mWidthGap = mOriginalWidthGap = 0;
- mHeightGap = mOriginalHeightGap = 0;
- mMaxGap = Integer.MAX_VALUE;
mCountX = grid.inv.numColumns;
mCountY = grid.inv.numRows;
@@ -287,8 +277,7 @@
}
mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context);
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
- mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
@@ -373,8 +362,7 @@
public void setCellDimensions(int width, int height) {
mFixedCellWidth = mCellWidth = width;
mFixedCellHeight = mCellHeight = height;
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
- mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
}
public void setGridSize(int x, int y) {
@@ -383,8 +371,7 @@
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
mTempRectStack.clear();
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
- mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
requestLayout();
}
@@ -618,7 +605,9 @@
public void setIsHotseat(boolean isHotseat) {
mIsHotseat = isHotseat;
- mShortcutsAndWidgets.setIsHotseat(isHotseat);
+ mShortcutsAndWidgets.setContainerType(isHotseat
+ ? ShortcutAndWidgetContainer.HOTSEAT
+ : ShortcutAndWidgetContainer.DEFAULT);
}
public boolean isHotseat() {
@@ -717,8 +706,8 @@
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
- result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
+ result[0] = (x - hStartPadding) / mCellWidth;
+ result[1] = (y - vStartPadding) / mCellHeight;
final int xAxis = mCountX;
final int yAxis = mCountY;
@@ -751,8 +740,8 @@
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
- result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
+ result[0] = hStartPadding + cellX * mCellWidth;
+ result[1] = vStartPadding + cellY * mCellHeight;
}
/**
@@ -778,10 +767,8 @@
void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) +
- (spanX * mCellWidth + (spanX - 1) * mWidthGap) / 2;
- result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) +
- (spanY * mCellHeight + (spanY - 1) * mHeightGap) / 2;
+ result[0] = hStartPadding + cellX * mCellWidth + (spanX * mCellWidth) / 2;
+ result[1] = vStartPadding + cellY * mCellHeight + (spanY * mCellHeight) / 2;
}
/**
@@ -794,10 +781,9 @@
void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) {
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- final int left = hStartPadding + cellX * (mCellWidth + mWidthGap);
- final int top = vStartPadding + cellY * (mCellHeight + mHeightGap);
- result.set(left, top, left + (spanX * mCellWidth + (spanX - 1) * mWidthGap),
- top + (spanY * mCellHeight + (spanY - 1) * mHeightGap));
+ final int left = hStartPadding + cellX * mCellWidth;
+ final int top = vStartPadding + cellY * mCellHeight;
+ result.set(left, top, left + (spanX * mCellWidth), top + (spanY * mCellHeight));
}
public float getDistanceFromCell(float x, float y, int[] cell) {
@@ -813,14 +799,6 @@
return mCellHeight;
}
- int getWidthGap() {
- return mWidthGap;
- }
-
- int getHeightGap() {
- return mHeightGap;
- }
-
public void setFixedSize(int width, int height) {
mFixedWidth = width;
mFixedHeight = height;
@@ -840,8 +818,7 @@
if (cw != mCellWidth || ch != mCellHeight) {
mCellWidth = cw;
mCellHeight = ch;
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap,
- mHeightGap, mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
}
}
@@ -854,23 +831,6 @@
throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
}
- int numWidthGaps = mCountX - 1;
- int numHeightGaps = mCountY - 1;
-
- if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
- int hSpace = childWidthSize;
- int vSpace = childHeightSize;
- int hFreeSpace = hSpace - (mCountX * mCellWidth);
- int vFreeSpace = vSpace - (mCountY * mCellHeight);
- mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
- mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap,
- mHeightGap, mCountX, mCountY);
- } else {
- mWidthGap = mOriginalWidthGap;
- mHeightGap = mOriginalHeightGap;
- }
-
// Make the feedback view large enough to hold the blur bitmap.
mTouchFeedbackView.measure(
MeasureSpec.makeMeasureSpec(mCellWidth + mTouchFeedbackView.getExtraSize(),
@@ -1083,23 +1043,19 @@
// 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;
+ left += ((mCellWidth * spanX) - 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;
+ left += dragOffset.x + ((mCellWidth * spanX) - 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;
+ left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
+ top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
}
}
r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
@@ -1188,8 +1144,8 @@
// For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
// to the center of the item, but we are searching based on the top-left cell, so
// we translate the point over to correspond to the top-left.
- pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
- pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
+ pixelX -= mCellWidth * (spanX - 1) / 2f;
+ pixelY -= mCellHeight * (spanY - 1) / 2f;
// Keep track of best-scoring drop area
final int[] bestXY = result != null ? result : new int[2];
@@ -2584,17 +2540,14 @@
public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, Rect resultRect) {
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
- final int widthGap = mWidthGap;
- final int heightGap = mHeightGap;
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
- int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
-
- int x = hStartPadding + cellX * (cellWidth + widthGap);
- int y = vStartPadding + cellY * (cellHeight + heightGap);
+ int width = cellHSpan * cellWidth;
+ int height = cellVSpan * cellHeight;
+ int x = hStartPadding + cellX * cellWidth;
+ int y = vStartPadding + cellY * cellHeight;
resultRect.set(x, y, x + width, y + height);
}
@@ -2612,13 +2565,11 @@
}
public int getDesiredWidth() {
- return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth) +
- (Math.max((mCountX - 1), 0) * mWidthGap);
+ return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth);
}
public int getDesiredHeight() {
- return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight) +
- (Math.max((mCountY - 1), 0) * mHeightGap);
+ return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight);
}
public boolean isOccupied(int x, int y) {
@@ -2738,8 +2689,7 @@
this.cellVSpan = cellVSpan;
}
- public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
- boolean invertHorizontally, int colCount) {
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount) {
if (isLockedToGrid) {
final int myCellHSpan = cellHSpan;
final int myCellVSpan = cellVSpan;
@@ -2750,12 +2700,10 @@
myCellX = colCount - myCellX - cellHSpan;
}
- width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
- leftMargin - rightMargin;
- height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
- topMargin - bottomMargin;
- x = (int) (myCellX * (cellWidth + widthGap) + leftMargin);
- y = (int) (myCellY * (cellHeight + heightGap) + topMargin);
+ width = myCellHSpan * cellWidth - leftMargin - rightMargin;
+ height = myCellVSpan * cellHeight - topMargin - bottomMargin;
+ x = (myCellX * cellWidth + leftMargin);
+ y = (myCellY * cellHeight + topMargin);
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index cc21920..59ec56a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -112,6 +112,7 @@
public int hotseatIconSizePx;
public int hotseatBarHeightPx;
private int hotseatBarTopPaddingPx;
+ private int hotseatBarBottomPaddingPx;
private int hotseatLandGutterPx;
// All apps
@@ -185,6 +186,7 @@
hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
hotseatBarTopPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
+ hotseatBarBottomPaddingPx = 0;
hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
// Determine sizes.
@@ -207,7 +209,18 @@
// In multi-window mode, we can have widthPx = availableWidthPx
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
- return new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y, isLandscape);
+ DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
+ isLandscape);
+
+ // Hide labels on the workspace.
+ profile.iconTextSizePx = 0;
+ profile.cellHeightPx = profile.iconSizePx + profile.iconDrawablePaddingPx
+ + Utilities.calculateTextHeight(profile.iconTextSizePx);
+
+ // The nav bar is black so we add bottom padding to visually center hotseat icons.
+ profile.hotseatBarBottomPaddingPx = profile.hotseatBarTopPaddingPx;
+
+ return profile;
}
public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
@@ -396,7 +409,8 @@
availablePaddingX = (int) Math.min(availablePaddingX,
width * MAX_HORIZONTAL_PADDING_PERCENT);
int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
- - (int) (2 * inv.numRows * cellHeightPx));
+ - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
+ - hotseatBarBottomPaddingPx);
padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
} else {
@@ -529,7 +543,7 @@
lp.height = hotseatBarHeightPx + mInsets.bottom;
hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
- mInsets.bottom);
+ hotseatBarBottomPaddingPx + mInsets.bottom);
} else {
// For phones, layout the hotseat without any bottom margin
// to ensure that we have space for the folders
@@ -538,7 +552,7 @@
lp.height = hotseatBarHeightPx + mInsets.bottom;
hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
- mInsets.bottom);
+ hotseatBarBottomPaddingPx + mInsets.bottom);
}
hotseat.setLayoutParams(lp);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0540249..f7d1f4a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -392,7 +392,7 @@
// LauncherModel load.
mPaused = false;
- setContentView(R.layout.launcher);
+ mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);
@@ -444,12 +444,18 @@
// we want the screen to auto-rotate based on the current orientation
setOrientation();
+ setContentView(mLauncherView);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
}
@Override
+ public View findViewById(int id) {
+ return mLauncherView.findViewById(id);
+ }
+
+ @Override
public void onExtractedColorsChanged() {
loadExtractedColorsAndColorItems();
}
@@ -1268,7 +1274,6 @@
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
- mLauncherView = findViewById(R.id.launcher);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
@@ -2946,10 +2951,9 @@
}
if (toState == State.APPS) {
- mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated,
- focusSearchBar);
+ mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
} else {
- mStateTransitionAnimation.startAnimationToWidgets(mWorkspace.getState(), animated);
+ mStateTransitionAnimation.startAnimationToWidgets(animated);
}
// Change the state *after* we've called all the transition code
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index d3e5350..657b024 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -20,10 +20,8 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.os.DeadObjectException;
-import android.os.TransactionTooLargeException;
+import android.util.SparseArray;
import android.view.LayoutInflater;
-import android.view.View;
import java.util.ArrayList;
@@ -36,6 +34,7 @@
public class LauncherAppWidgetHost extends AppWidgetHost {
private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
+ private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
private Launcher mLauncher;
@@ -45,9 +44,11 @@
}
@Override
- protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+ protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- return new LauncherAppWidgetHostView(context);
+ LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+ mViews.put(appWidgetId, view);
+ return view;
}
@Override
@@ -55,15 +56,13 @@
try {
super.startListening();
} catch (Exception e) {
- if (e.getCause() instanceof TransactionTooLargeException ||
- e.getCause() instanceof DeadObjectException) {
- // We're willing to let this slide. The exception is being caused by the list of
- // RemoteViews which is being passed back. The startListening relationship will
- // have been established by this point, and we will end up populating the
- // widgets upon bind anyway. See issue 14255011 for more context.
- } else {
+ if (!Utilities.isBinderSizeError(e)) {
throw new RuntimeException(e);
}
+ // We're willing to let this slide. The exception is being caused by the list of
+ // RemoteViews which is being passed back. The startListening relationship will
+ // have been established by this point, and we will end up populating the
+ // widgets upon bind anyway. See issue 14255011 for more context.
}
}
@@ -98,7 +97,24 @@
lahv.updateLastInflationOrientation();
return lahv;
} else {
- return super.createView(context, appWidgetId, appWidget);
+ try {
+ return super.createView(context, appWidgetId, appWidget);
+ } catch (Exception e) {
+ if (!Utilities.isBinderSizeError(e)) {
+ throw new RuntimeException(e);
+ }
+
+ // If the exception was thrown while fetching the remote views, let the view stay.
+ // This will ensure that if the widget posts a valid update later, the view
+ // will update.
+ LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+ if (view == null) {
+ view = onCreateView(mLauncher, appWidgetId, appWidget);
+ }
+ view.setAppWidget(appWidgetId, appWidget);
+ view.switchToErrorView();
+ return view;
+ }
}
}
@@ -114,4 +130,16 @@
// launcher spans accordingly.
info.initSpans();
}
+
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) {
+ super.deleteAppWidgetId(appWidgetId);
+ mViews.remove(appWidgetId);
+ }
+
+ @Override
+ protected void clearViews() {
+ super.clearViews();
+ mViews.clear();
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index b34d1ff..a4ea449 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -77,7 +77,7 @@
mContext = context;
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
@@ -316,6 +316,11 @@
setSelected(childIsFocused);
}
+ public void switchToErrorView() {
+ // Update the widget with 0 Layout id, to reset the view to error view.
+ updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
@@ -324,8 +329,7 @@
post(new Runnable() {
@Override
public void run() {
- // Update the widget with 0 Layout id, to reset the view to error view.
- updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+ switchToErrorView();
}
});
}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 7e84264..19cc0fb 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -23,13 +23,13 @@
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-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.Animation;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -135,7 +135,7 @@
* @param startSearchAfterTransition Immediately starts app search after the transition to
* All Apps is completed.
*/
- public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
+ public void startAnimationToAllApps(
final boolean animated, final boolean startSearchAfterTransition) {
final AllAppsContainerView toView = mLauncher.getAppsView();
final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
@@ -170,18 +170,17 @@
animType = PULLUP;
}
// Only animate the search bar if animating from spring loaded mode back to all apps
- startAnimationToOverlay(fromWorkspaceState,
+ startAnimationToOverlay(
Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb);
}
/**
* Starts an animation to the widgets view.
*/
- public void startAnimationToWidgets(final Workspace.State fromWorkspaceState,
- final boolean animated) {
+ public void startAnimationToWidgets(final boolean animated) {
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
- startAnimationToOverlay(fromWorkspaceState,
+ startAnimationToOverlay(
Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL,
new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
@Override
@@ -224,9 +223,8 @@
/**
* Creates and starts a new animation to a particular overlay view.
*/
- @SuppressLint("NewApi")
private void startAnimationToOverlay(
- final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+ final Workspace.State toWorkspaceState,
final View buttonView, final BaseContainerView toView,
final boolean animated, int animType, final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
@@ -237,8 +235,6 @@
final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
- final View fromView = mLauncher.getWorkspace();
-
final AnimationLayerSet layerViews = new AnimationLayerSet();
// If for some reason our views aren't initialized, don't animate
@@ -248,7 +244,7 @@
cancelAnimation();
final View contentView = toView.getContentView();
- playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ playCommonTransitionAnimations(toWorkspaceState,
animated, initialized, animation, layerViews);
if (!animated || !initialized) {
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
@@ -264,13 +260,6 @@
// Show the content view
contentView.setVisibility(View.VISIBLE);
-
- dispatchOnLauncherTransitionPrepare(fromView, animated, false);
- dispatchOnLauncherTransitionStart(fromView, animated, false);
- dispatchOnLauncherTransitionEnd(fromView, animated, false);
- dispatchOnLauncherTransitionPrepare(toView, animated, false);
- dispatchOnLauncherTransitionStart(toView, animated, false);
- dispatchOnLauncherTransitionEnd(toView, animated, false);
pCb.onTransitionComplete();
return;
}
@@ -355,9 +344,6 @@
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- dispatchOnLauncherTransitionEnd(fromView, animated, false);
- dispatchOnLauncherTransitionEnd(toView, animated, false);
-
// Hide the reveal view
revealView.setVisibility(View.INVISIBLE);
@@ -368,31 +354,11 @@
});
- // Dispatch the prepare transition signal
- dispatchOnLauncherTransitionPrepare(fromView, animated, false);
- dispatchOnLauncherTransitionPrepare(toView, animated, false);
-
- final AnimatorSet stateAnimation = animation;
- final Runnable startAnimRunnable = new Runnable() {
- public void run() {
- // Check that mCurrentAnimation hasn't changed while
- // we waited for a layout/draw pass
- if (mCurrentAnimation != stateAnimation)
- return;
- dispatchOnLauncherTransitionStart(fromView, animated, false);
- dispatchOnLauncherTransitionStart(toView, animated, false);
-
- // Focus the new view
- toView.requestFocus();
-
- stateAnimation.start();
- }
- };
toView.bringToFront();
toView.setVisibility(View.VISIBLE);
animation.addListener(layerViews);
- toView.post(startAnimRunnable);
+ toView.post(new StartAnimRunnable(animation, toView));
mCurrentAnimation = animation;
} else if (animType == PULLUP) {
// We are animating the content view alpha, so ensure we have a layer for it
@@ -401,32 +367,13 @@
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- dispatchOnLauncherTransitionEnd(fromView, animated, false);
- dispatchOnLauncherTransitionEnd(toView, animated, false);
cleanupAnimation();
pCb.onTransitionComplete();
}
});
boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide);
- dispatchOnLauncherTransitionPrepare(fromView, animated, false);
- dispatchOnLauncherTransitionPrepare(toView, animated, false);
-
- final AnimatorSet stateAnimation = animation;
- final Runnable startAnimRunnable = new Runnable() {
- public void run() {
- // Check that mCurrentAnimation hasn't changed while
- // we waited for a layout/draw pass
- if (mCurrentAnimation != stateAnimation)
- return;
-
- dispatchOnLauncherTransitionStart(fromView, animated, false);
- dispatchOnLauncherTransitionStart(toView, animated, false);
-
- toView.requestFocus();
- stateAnimation.start();
- }
- };
+ Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
mCurrentAnimation = animation;
mCurrentAnimation.addListener(layerViews);
if (shouldPost) {
@@ -441,7 +388,7 @@
* Plays animations used by various transitions.
*/
private void playCommonTransitionAnimations(
- Workspace.State toWorkspaceState, View fromView, View toView,
+ Workspace.State toWorkspaceState,
boolean animated, boolean initialized, AnimatorSet animation,
AnimationLayerSet layerViews) {
// Create the workspace animation.
@@ -454,36 +401,16 @@
if (workspaceAnim != null) {
animation.play(workspaceAnim);
}
- // Dispatch onLauncherTransitionStep() as the animation interpolates.
- animation.play(dispatchOnLauncherTransitionStepAnim(fromView, toView));
}
}
/**
- * Returns an Animator that calls {@link #dispatchOnLauncherTransitionStep(View, float)} on
- * {@param fromView} and {@param toView} as the animation interpolates.
- *
- * This is a bit hacky: we create a dummy ValueAnimator just for the AnimatorUpdateListener.
- */
- private Animator dispatchOnLauncherTransitionStepAnim(final View fromView, final View toView) {
- ValueAnimator updateAnimator = ValueAnimator.ofFloat(0, 1);
- updateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- dispatchOnLauncherTransitionStep(fromView, animation.getAnimatedFraction());
- dispatchOnLauncherTransitionStep(toView, animation.getAnimatedFraction());
- }
- });
- return updateAnimator;
- }
-
- /**
* Starts an animation to the workspace from the apps view.
*/
private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final boolean animated, int type,
final Runnable onCompleteRunnable) {
- AllAppsContainerView appsView = mLauncher.getAppsView();
+ final AllAppsContainerView appsView = mLauncher.getAppsView();
// No alpha anim from all apps
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@Override
@@ -513,6 +440,7 @@
@Override
void onTransitionComplete() {
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ appsView.reset();
}
};
// Only animate the search bar if animating to spring loaded mode from all apps
@@ -564,34 +492,13 @@
// Cancel the current animation
cancelAnimation();
- boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
-
- playCommonTransitionAnimations(toWorkspaceState, fromWorkspace, null,
- animated, animated, animation, layerViews);
-
+ playCommonTransitionAnimations(toWorkspaceState, animated, animated, animation, layerViews);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
if (animated) {
- dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, multiplePagesVisible);
-
- 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);
- 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();
@@ -601,14 +508,10 @@
cleanupAnimation();
}
});
- stateAnimation.addListener(layerViews);
- fromWorkspace.post(startAnimRunnable);
+ animation.addListener(layerViews);
+ fromWorkspace.post(new StartAnimRunnable(animation, null));
mCurrentAnimation = animation;
} else /* if (!animated) */ {
- dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionStart(fromWorkspace, animated, true);
- dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true);
-
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
@@ -646,9 +549,7 @@
// Cancel the current animation
cancelAnimation();
- boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
-
- playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ playCommonTransitionAnimations(toWorkspaceState,
animated, initialized, animation, layerViews);
if (!animated || !initialized) {
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
@@ -656,12 +557,6 @@
mAllAppsController.finishPullDown();
}
fromView.setVisibility(View.GONE);
- dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionStart(fromView, animated, true);
- dispatchOnLauncherTransitionEnd(fromView, animated, true);
- dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionStart(toView, animated, true);
- dispatchOnLauncherTransitionEnd(toView, animated, true);
pCb.onTransitionComplete();
// Run any queued runnables
@@ -774,16 +669,10 @@
}
}
- dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
-
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
fromView.setVisibility(View.GONE);
- dispatchOnLauncherTransitionEnd(fromView, animated, true);
- dispatchOnLauncherTransitionEnd(toView, animated, true);
-
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
@@ -802,23 +691,9 @@
}
});
- 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(fromView, animated, false);
- dispatchOnLauncherTransitionStart(toView, animated, false);
- stateAnimation.start();
- }
- };
mCurrentAnimation = animation;
mCurrentAnimation.addListener(layerViews);
- fromView.post(startAnimRunnable);
+ fromView.post(new StartAnimRunnable(animation, null));
} else if (animType == PULLUP) {
// We are animating the content view alpha, so ensure we have a layer for it
layerViews.addView(contentView);
@@ -833,8 +708,6 @@
@Override
public void onAnimationEnd(Animator animation) {
if (canceled) return;
- dispatchOnLauncherTransitionEnd(fromView, animated, true);
- dispatchOnLauncherTransitionEnd(toView, animated, true);
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
@@ -847,26 +720,7 @@
});
boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
- // Dispatch the prepare transition signal
- dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
-
- final AnimatorSet stateAnimation = animation;
- final Runnable startAnimRunnable = new Runnable() {
- public void run() {
- // Check that mCurrentAnimation hasn't changed while
- // we waited for a layout/draw pass
- if (mCurrentAnimation != stateAnimation)
- return;
-
- dispatchOnLauncherTransitionStart(fromView, animated, false);
- dispatchOnLauncherTransitionStart(toView, animated, false);
-
- // Focus the new view
- toView.requestFocus();
- stateAnimation.start();
- }
- };
+ Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
mCurrentAnimation = animation;
mCurrentAnimation.addListener(layerViews);
if (shouldPost) {
@@ -879,52 +733,6 @@
}
/**
- * Dispatches the prepare-transition event to suitable views.
- */
- void dispatchOnLauncherTransitionPrepare(View v, boolean animated,
- boolean multiplePagesVisible) {
- if (v instanceof LauncherTransitionable) {
- ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
- multiplePagesVisible);
- }
- }
-
- /**
- * Dispatches the start-transition event to suitable views.
- */
- void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
- if (v instanceof LauncherTransitionable) {
- ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
- toWorkspace);
- }
-
- // Update the workspace transition step as well
- dispatchOnLauncherTransitionStep(v, 0f);
- }
-
- /**
- * Dispatches the step-transition event to suitable views.
- */
- void dispatchOnLauncherTransitionStep(View v, float t) {
- if (v instanceof LauncherTransitionable) {
- ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
- }
- }
-
- /**
- * Dispatches the end-transition event to suitable views.
- */
- void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
- if (v instanceof LauncherTransitionable) {
- ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
- toWorkspace);
- }
-
- // Update the workspace transition step as well
- dispatchOnLauncherTransitionStep(v, 1f);
- }
-
- /**
* Cancels the current animation.
*/
private void cancelAnimation() {
@@ -938,4 +746,26 @@
@Thunk void cleanupAnimation() {
mCurrentAnimation = null;
}
+
+ private class StartAnimRunnable implements Runnable {
+
+ private final AnimatorSet mAnim;
+ private final View mViewToFocus;
+
+ public StartAnimRunnable(AnimatorSet anim, View viewToFocus) {
+ mAnim = anim;
+ mViewToFocus = viewToFocus;
+ }
+
+ @Override
+ public void run() {
+ if (mCurrentAnimation != mAnim) {
+ return;
+ }
+ if (mViewToFocus != null) {
+ mViewToFocus.requestFocus();
+ }
+ mAnim.start();
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherTransitionable.java b/src/com/android/launcher3/LauncherTransitionable.java
deleted file mode 100644
index b97aaec..0000000
--- a/src/com/android/launcher3/LauncherTransitionable.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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;
-
-/**
- * An interface to get callbacks during a launcher transition.
- */
-public interface LauncherTransitionable {
- void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean multiplePagesVisible);
- void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
- void onLauncherTransitionStep(Launcher l, float t);
- void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
-}
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index 3cc0f7d..c1d60fd 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -108,7 +108,7 @@
public void onAnimationEnd(Animator animation) {
mIsAnimating = false;
thresholdManager.reset();
- mWorkspace.onLauncherTransitionEnd(mLauncher, false, true);
+ mWorkspace.onEndStateTransition();
}
});
animator.setDuration(duration).start();
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
index 66209bf..42515d1 100644
--- a/src/com/android/launcher3/PinchToOverviewListener.java
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -112,7 +112,7 @@
mInterpolator = mWorkspace.isInOverviewMode() ? new LogDecelerateInterpolator(100, 0)
: new LogAccelerateInterpolator(100, 0);
mPinchStarted = true;
- mWorkspace.onLauncherTransitionPrepare(mLauncher, false, true);
+ mWorkspace.onPrepareStateTransition(true);
return true;
}
@@ -142,7 +142,7 @@
mThresholdManager);
} else {
mThresholdManager.reset();
- mWorkspace.onLauncherTransitionEnd(mLauncher, false, true);
+ mWorkspace.onEndStateTransition();
}
mPinchStarted = false;
mPinchCanceled = false;
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index c8bb793..5f89af6 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,13 +18,25 @@
import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Paint;
import android.graphics.Rect;
+import android.support.annotation.IntDef;
import android.view.View;
import android.view.ViewGroup;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
public class ShortcutAndWidgetContainer extends ViewGroup {
- static final String TAG = "CellLayoutChildren";
+ static final String TAG = "ShortcutAndWidgetContainer";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DEFAULT, HOTSEAT, FOLDER})
+ public @interface ContainerType{}
+ public static final int DEFAULT = 0;
+ public static final int HOTSEAT = 1;
+ public static final int FOLDER = 2;
+
+ private int mContainerType = DEFAULT;
// 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.
@@ -32,14 +44,9 @@
private final WallpaperManager mWallpaperManager;
- private boolean mIsHotseatLayout;
-
private int mCellWidth;
private int mCellHeight;
- private int mWidthGap;
- private int mHeightGap;
-
private int mCountX;
private Launcher mLauncher;
@@ -52,12 +59,9 @@
mWallpaperManager = WallpaperManager.getInstance(context);
}
- public void setCellDimensions(int cellWidth, int cellHeight, int widthGap, int heightGap,
- int countX, int countY) {
+ public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY) {
mCellWidth = cellWidth;
mCellHeight = cellHeight;
- mWidthGap = widthGap;
- mHeightGap = heightGap;
mCountX = countX;
}
@@ -92,8 +96,7 @@
}
public void setupLp(CellLayout.LayoutParams lp) {
- lp.setup(mCellWidth, mCellHeight, mWidthGap, mHeightGap, invertLayoutHorizontally(),
- mCountX);
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
}
// Set whether or not to invert the layout horizontally if the layout is in RTL mode.
@@ -101,20 +104,19 @@
mInvertIfRtl = invert;
}
- public void setIsHotseat(boolean isHotseat) {
- mIsHotseatLayout = isHotseat;
- }
-
- int getCellContentWidth() {
- final DeviceProfile grid = mLauncher.getDeviceProfile();
- return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
- grid.hotseatCellWidthPx: grid.cellWidthPx);
+ public void setContainerType(@ContainerType int containerType) {
+ mContainerType = containerType;
}
int getCellContentHeight() {
final DeviceProfile grid = mLauncher.getDeviceProfile();
- return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
- grid.hotseatCellHeightPx : grid.cellHeightPx);
+ int cellContentHeight = grid.cellHeightPx;
+ if (mContainerType == HOTSEAT) {
+ cellContentHeight = grid.hotseatCellHeightPx;
+ } else if (mContainerType == FOLDER) {
+ cellContentHeight = grid.folderCellHeightPx;
+ }
+ return Math.min(getMeasuredHeight(), cellContentHeight);
}
public void measureChild(View child) {
@@ -123,8 +125,7 @@
final int cellHeight = mCellHeight;
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (!lp.isFullscreen) {
- lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, invertLayoutHorizontally(),
- mCountX);
+ lp.setup(cellWidth, cellHeight, invertLayoutHorizontally(), mCountX);
if (child instanceof LauncherAppWidgetHostView) {
// Widgets have their own padding, so skip
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index e82c32e..416ca8e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -38,7 +38,9 @@
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.PowerManager;
+import android.os.TransactionTooLargeException;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -648,4 +650,9 @@
accessibilityManager.sendAccessibilityEvent(event);
}
}
+
+ public static boolean isBinderSizeError(Exception e) {
+ return e.getCause() instanceof TransactionTooLargeException
+ || e.getCause() instanceof DeadObjectException;
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 95a5ee2..01d87f4 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -48,6 +48,7 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Animation.AnimationListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -96,7 +97,7 @@
*/
public class Workspace extends PagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
- DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
+ DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
Insettable, DropTargetSource {
private static final String TAG = "Launcher.Workspace";
@@ -2048,6 +2049,20 @@
mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
}
+ onPrepareStateTransition(mState.hasMultipleVisiblePages);
+
+ StateTransitionListener listener = new StateTransitionListener();
+ if (animated) {
+ ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
+ stepAnimator.addListener(listener);
+
+ workspaceAnim.play(stepAnimator);
+ workspaceAnim.addListener(listener);
+ } else {
+ listener.onAnimationStart(null);
+ listener.onAnimationEnd(null);
+ }
+
return workspaceAnim;
}
@@ -2100,9 +2115,7 @@
}
}
- @Override
- public void onLauncherTransitionPrepare(Launcher l, boolean animated,
- boolean multiplePagesVisible) {
+ public void onPrepareStateTransition(boolean multiplePagesVisible) {
mIsSwitchingState = true;
mTransitionProgress = 0;
@@ -2115,32 +2128,12 @@
hideCustomContentIfNecessary();
}
- @Override
- public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
- if (mPageIndicator != null) {
- boolean isNewStateSpringLoaded = mState == State.SPRING_LOADED;
- mPageIndicator.setShouldAutoHide(!isNewStateSpringLoaded);
- if (isNewStateSpringLoaded) {
- // Show the page indicator at the same time as the rest of the transition.
- showPageIndicatorAtCurrentScroll();
- }
- }
- }
-
- @Override
- public void onLauncherTransitionStep(Launcher l, float t) {
- mTransitionProgress = t;
- }
-
- @Override
- public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
+ public void onEndStateTransition() {
mIsSwitchingState = false;
updateChildrenLayersEnabled(false);
showCustomContentIfNecessary();
mForceDrawAdjacentPages = false;
- if (mState == State.SPRING_LOADED) {
- showPageIndicatorAtCurrentScroll();
- }
+ mTransitionProgress = 1;
}
void updateCustomContentVisibility() {
@@ -2528,6 +2521,7 @@
onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
} else if (mDragInfo != null) {
final View cell = mDragInfo.cell;
+ boolean droppedOnOriginalCellDuringTransition = false;
if (dropTargetLayout != null && !d.cancelled) {
// Move internally
@@ -2570,6 +2564,10 @@
minSpanY = item.minSpanY;
}
+ droppedOnOriginalCellDuringTransition = mIsSwitchingState
+ && item.screenId == screenId && item.container == container
+ && item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];
+
int[] resultSpan = new int[2];
mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
@@ -2627,7 +2625,7 @@
&& !d.accessibleDrag) {
mDelayedResizeRunnable = new Runnable() {
public void run() {
- if (!isPageInTransition() && !mIsSwitchingState) {
+ if (!isPageInTransition()) {
DragLayer dragLayer = mLauncher.getDragLayer();
dragLayer.addResizeFrame(hostView, cellLayout);
}
@@ -2662,6 +2660,17 @@
};
mAnimatingViewIntoPlace = true;
if (d.dragView.hasDrawn()) {
+ if (droppedOnOriginalCellDuringTransition) {
+ // Animate the item to its original position, while simultaneously exiting
+ // spring-loaded mode so the page meets the icon where it was picked up.
+ mLauncher.getDragController().animateDragViewToOriginalPosition(
+ mDelayedResizeRunnable, cell,
+ mStateTransitionAnimation.mSpringLoadedTransitionTime);
+ mLauncher.exitSpringLoadedDragMode();
+ mLauncher.getDropTargetBar().onDragEnd();
+ parent.onDropChild(cell);
+ return;
+ }
final ItemInfo info = (ItemInfo) cell.getTag();
boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
@@ -4275,4 +4284,26 @@
public static final boolean isQsbContainerPage(int pageNo) {
return pageNo == 0;
}
+
+ private class StateTransitionListener extends AnimatorListenerAdapter
+ implements AnimatorUpdateListener {
+ @Override
+ public void onAnimationUpdate(ValueAnimator anim) {
+ mTransitionProgress = anim.getAnimatedFraction();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mState == State.SPRING_LOADED) {
+ // Show the page indicator at the same time as the rest of the transition.
+ showPageIndicatorAtCurrentScroll();
+ }
+ mTransitionProgress = 0;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onEndStateTransition();
+ }
+ }
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 1cf4b39..1f36468 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -422,6 +422,11 @@
}
@Override
+ public void onAnimationStart(Animator animation) {
+ mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
mStateAnimator = null;
if (canceled) return;
@@ -434,6 +439,7 @@
} else {
overviewPanel.setAlpha(finalOverviewPanelAlpha);
AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
+ mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
qsbAlphaAnimation.end();
mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c65a803..5fc1d97 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -43,7 +43,6 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherTransitionable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
@@ -63,8 +62,7 @@
* The all apps view container.
*/
public class AllAppsContainerView extends BaseContainerView implements DragSource,
- LauncherTransitionable, View.OnLongClickListener, AllAppsSearchBarController.Callbacks,
- Insettable {
+ View.OnLongClickListener, AllAppsSearchBarController.Callbacks, Insettable {
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
@@ -75,6 +73,7 @@
private AllAppsSearchBarController mSearchBarController;
private View mSearchContainer;
+ private int mSearchContainerMinHeight;
private ExtendedEditText mSearchInput;
private HeaderElevationController mElevationController;
@@ -100,6 +99,9 @@
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mSearchQueryBuilder = new SpannableStringBuilder();
+ mSearchContainerMinHeight
+ = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
+
Selection.setSelection(mSearchQueryBuilder, 0);
}
@@ -315,8 +317,9 @@
if (!grid.isVerticalBarLayout()) {
MarginLayoutParams searchContainerLp =
(MarginLayoutParams) mSearchContainer.getLayoutParams();
+
searchContainerLp.height = mLauncher.getDragLayer().getInsets().top
- + grid.hotseatCellHeightPx;
+ + mSearchContainerMinHeight;
mSearchContainer.setLayoutParams(searchContainerLp);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -445,29 +448,6 @@
}
@Override
- public void onLauncherTransitionPrepare(Launcher l, boolean animated,
- boolean multiplePagesVisible) {
- // Do nothing
- }
-
- @Override
- public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
- // Do nothing
- }
-
- @Override
- public void onLauncherTransitionStep(Launcher l, float t) {
- // Do nothing
- }
-
- @Override
- public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
- if (toWorkspace) {
- reset();
- }
- }
-
- @Override
public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
if (apps != null) {
if (mApps.setOrderedFilter(apps)) {
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index e11bfc6..67ef5fc 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -295,10 +295,6 @@
mIsInPreDrag = false;
}
- public boolean isInPreDrag() {
- return mIsInPreDrag;
- }
-
/**
* Call this from a drag source view like this:
*
@@ -361,6 +357,8 @@
isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
if (!isDeferred) {
mDragObject.dragView.remove();
+ } else if (mIsInPreDrag) {
+ animateDragViewToOriginalPosition(null, null, -1);
}
mDragObject.dragView = null;
}
@@ -374,6 +372,22 @@
releaseVelocityTracker();
}
+ public void animateDragViewToOriginalPosition(final Runnable onComplete,
+ final View originalIcon, int duration) {
+ Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (originalIcon != null) {
+ originalIcon.setVisibility(View.VISIBLE);
+ }
+ if (onComplete != null) {
+ onComplete.run();
+ }
+ }
+ };
+ mDragObject.dragView.animateTo(mMotionDownX, mMotionDownY, onCompleteRunnable, duration);
+ }
+
private void callOnDragEnd() {
if (mIsInPreDrag && mOptions.preDragCondition != null) {
mOptions.preDragCondition.onPreDragEnd(false /* dragStarted*/);
@@ -738,7 +752,7 @@
if (dropTarget.acceptDrop(mDragObject)) {
if (flingVel != null) {
dropTarget.onFlingToDelete(mDragObject, flingVel);
- } else {
+ } else if (!mIsInPreDrag) {
dropTarget.onDrop(mDragObject);
}
accepted = true;
@@ -749,11 +763,6 @@
if (!mIsInPreDrag) {
mDragObject.dragSource.onDropCompleted(
dropTargetAsView, mDragObject, flingVel != null, accepted);
- } else {
- // Only defer the drag view cleanup if the drag source handles the drop.
- if (!(mDragObject.dragSource instanceof DropTarget)) {
- mDragObject.deferDragViewCleanupPostAnimation = false;
- }
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 8a2ae94..22e077b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -23,7 +23,6 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -38,11 +37,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Thunk;
-
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Thunk;
import java.util.Arrays;
@@ -57,6 +54,8 @@
@Thunk Paint mPaint;
private final int mRegistrationX;
private final int mRegistrationY;
+ private final float mInitialScale;
+ private final int[] mTempLoc = new int[2];
private Point mDragVisualizeOffset = null;
private Rect mDragRegion = null;
@@ -138,6 +137,8 @@
mRegistrationX = registrationX;
mRegistrationY = registrationY;
+ mInitialScale = initialScale;
+
// Force a measure, because Workspace uses getMeasuredHeight() before the layout pass
int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
measure(ms, ms);
@@ -356,6 +357,13 @@
applyTranslation();
}
+ public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+ mTempLoc[0] = toTouchX - mRegistrationX;
+ mTempLoc[1] = toTouchY - mRegistrationY;
+ mDragLayer.animateViewIntoPosition(this, mTempLoc, 1f, mInitialScale, mInitialScale,
+ DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
+ }
+
public void animateShift(final int shiftX, final int shiftY) {
if (mAnim.isStarted()) {
return;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2952196..53c12b5 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1331,10 +1331,7 @@
mIsExternalDrag = false;
} else {
currentDragView = mCurrentDragView;
- // The view was never removed from this folder if we are still in the pre-drag.
- if (!mDragController.isInPreDrag()) {
- mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
- }
+ mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
}
if (d.dragView.hasDrawn()) {
@@ -1355,12 +1352,9 @@
mItemsInvalidated = true;
rearrangeChildren();
- // The ShortcutInfo was never removed if we are still in the pre-drag.
- if (!mDragController.isInPreDrag()) {
- // Temporarily suppress the listener, as we did all the work already here.
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, false);
- }
+ // Temporarily suppress the listener, as we did all the work already here.
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.add(si, false);
}
// Clear the drag info, as it is no longer being dragged.
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 31ec709..e71c5e9 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -256,6 +256,7 @@
CellLayout page = new CellLayout(getContext());
page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
+ page.getShortcutsAndWidgets().setContainerType(ShortcutAndWidgetContainer.FOLDER);
page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
page.setInvertIfRtl(true);
page.setGridSize(mGridCountX, mGridCountY);
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 668b21b..176e8ea 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -87,6 +87,7 @@
final int N = packages.length;
FlagOp flagOp = FlagOp.NO_OP;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
+ ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
switch (mOp) {
case OP_ADD: {
for (int i = 0; i < N; i++) {
@@ -135,15 +136,15 @@
FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
- appsList.updateDisabledFlags(
- ItemInfoMatcher.ofPackages(packageSet, mUser), flagOp);
+ appsList.updateDisabledFlags(matcher, flagOp);
break;
case OP_USER_AVAILABILITY_CHANGE:
flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
// We want to update all packages for this user.
- appsList.updateDisabledFlags(ItemInfoMatcher.ofUser(mUser), flagOp);
+ matcher = ItemInfoMatcher.ofUser(mUser);
+ appsList.updateDisabledFlags(matcher, flagOp);
break;
}
@@ -218,10 +219,10 @@
}
ComponentName cn = si.getTargetComponent();
- if (cn != null && packageSet.contains(cn.getPackageName())) {
+ if (cn != null && matcher.matches(si, cn)) {
AppInfo appInfo = addedOrUpdatedApps.get(cn);
- if (si.isPromise()) {
+ if (si.isPromise() && mOp == OP_ADD) {
if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
// Auto install icon
PackageManager pm = context.getPackageManager();
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 3953f39..64043f4 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -6,17 +6,14 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.DeadObjectException;
-import android.os.TransactionTooLargeException;
import android.util.Log;
import com.android.launcher3.AppFilter;
import com.android.launcher3.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.config.ProviderConfig;
@@ -24,10 +21,7 @@
import com.android.launcher3.util.Preconditions;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
@@ -80,9 +74,7 @@
}
setWidgetsAndShortcuts(widgetsAndShortcuts);
} catch (Exception e) {
- if (!ProviderConfig.IS_DOGFOOD_BUILD &&
- (e.getCause() instanceof TransactionTooLargeException ||
- e.getCause() instanceof DeadObjectException)) {
+ if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
// time Launcher starts.
// TODO: after figuring out a repro step, introduce a dirty bit to check when
diff --git a/tests/src/com/android/launcher3/ui/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/AddWidgetTest.java
index a0ca60c..d536af2 100644
--- a/tests/src/com/android/launcher3/ui/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/AddWidgetTest.java
@@ -5,7 +5,6 @@
import android.test.suitebuilder.annotation.LargeTest;
import android.view.View;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetInfo;
@@ -51,7 +50,7 @@
// Drag widget to homescreen
UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
.hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
- dragToWorkspace(widget);
+ dragToWorkspace(widget, false);
assertNotNull(launcher.getWorkspace().getFirstMatch(new ItemOperator() {
@Override
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
index 56fc90a..f45710c 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -47,7 +47,7 @@
// Drag icon to homescreen.
UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
- dragToWorkspace(icon);
+ dragToWorkspace(icon, true);
// Verify that the icon works on homescreen.
mDevice.findObject(By.text(mSettingsApp.getLabel().toString())).click();
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
index e94fca6..fcf7122 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -140,29 +140,52 @@
/**
* Drags an icon to the center of homescreen.
*/
- protected void dragToWorkspace(UiObject2 icon) {
+ protected void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
Point center = icon.getVisibleCenter();
// Action Down
sendPointer(MotionEvent.ACTION_DOWN, center);
- // Wait until "Remove/Delete target is visible
+ UiObject2 dragLayer = findViewById(R.id.drag_layer);
+
+ if (expectedToShowShortcuts) {
+ // Make sure shortcuts show up, and then move a bit to hide them.
+ assertNotNull(findViewById(R.id.deep_shortcuts_container));
+
+ Point moveLocation = new Point(center);
+ int distanceToMove = mTargetContext.getResources().getDimensionPixelSize(
+ R.dimen.deep_shortcuts_start_drag_threshold) + 50;
+ if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
+ moveLocation.y -= distanceToMove;
+ } else {
+ moveLocation.y += distanceToMove;
+ }
+ movePointer(center, moveLocation);
+
+ assertNull(findViewById(R.id.deep_shortcuts_container));
+ }
+
+ // Wait until Remove/Delete target is visible
assertNotNull(findViewById(R.id.delete_target_text));
- Point moveLocation = findViewById(R.id.drag_layer).getVisibleCenter();
+ Point moveLocation = dragLayer.getVisibleCenter();
// Move to center
- while(!moveLocation.equals(center)) {
- center.x = getNextMoveValue(moveLocation.x, center.x);
- center.y = getNextMoveValue(moveLocation.y, center.y);
- sendPointer(MotionEvent.ACTION_MOVE, center);
- }
+ movePointer(center, moveLocation);
sendPointer(MotionEvent.ACTION_UP, center);
// Wait until remove target is gone.
mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
}
+ private void movePointer(Point from, Point to) {
+ while(!from.equals(to)) {
+ from.x = getNextMoveValue(to.x, from.x);
+ from.y = getNextMoveValue(to.y, from.y);
+ sendPointer(MotionEvent.ACTION_MOVE, from);
+ }
+ }
+
private int getNextMoveValue(int targetValue, int oldValue) {
if (targetValue - oldValue > 10) {
return oldValue + 10;
@@ -173,7 +196,7 @@
}
}
- private void sendPointer(int action, Point point) {
+ protected void sendPointer(int action, Point point) {
MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), action, point.x, point.y, 0);
getInstrumentation().sendPointerSync(event);
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
new file mode 100644
index 0000000..f892e63
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -0,0 +1,68 @@
+package com.android.launcher3.ui;
+
+import android.graphics.Point;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for verifying that shortcuts are shown and can be launched after long pressing an app
+ */
+@LargeTest
+public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase {
+
+ private LauncherActivityInfoCompat mSettingsApp;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+ .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+ }
+
+ public void testAppLauncher_portrait() throws Exception {
+ lockRotation(true);
+ performTest();
+ }
+
+ public void testAppLauncher_landscape() throws Exception {
+ lockRotation(false);
+ performTest();
+ }
+
+ private void performTest() throws Exception {
+ startLauncher();
+
+ // Open all apps and wait for load complete
+ final UiObject2 appsContainer = openAllApps();
+ assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ // Find settings app and verify shortcuts appear when long pressed
+ UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
+ // Press icon center until shortcuts appear
+ Point iconCenter = icon.getVisibleCenter();
+ sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
+ UiObject2 deepShortcutsContainer = findViewById(R.id.deep_shortcuts_container);
+ assertNotNull(deepShortcutsContainer);
+ sendPointer(MotionEvent.ACTION_UP, iconCenter);
+
+ // Verify that launching a shortcut opens a page with the same text
+ assertTrue(deepShortcutsContainer.getChildCount() > 0);
+ UiObject2 shortcut = deepShortcutsContainer.getChildren().get(0)
+ .findObject(getSelectorForId(R.id.deep_shortcut));
+ shortcut.click();
+ assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+ mSettingsApp.getComponentName().getPackageName())
+ .text(shortcut.getText())), DEFAULT_UI_TIMEOUT));
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
new file mode 100644
index 0000000..f9a0e6e
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -0,0 +1,74 @@
+package com.android.launcher3.ui;
+
+import android.graphics.Point;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for dragging a deep shortcut to the home screen.
+ */
+@LargeTest
+public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase {
+
+ private LauncherActivityInfoCompat mSettingsApp;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+ .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+ }
+
+ public void testDragIcon_portrait() throws Throwable {
+ lockRotation(true);
+ performTest();
+ }
+
+ public void testDragIcon_landscape() throws Throwable {
+ lockRotation(false);
+ performTest();
+ }
+
+ private void performTest() throws Throwable {
+ clearHomescreen();
+ startLauncher();
+
+ // Open all apps and wait for load complete.
+ final UiObject2 appsContainer = openAllApps();
+ assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ // Find the app and long press it to show shortcuts.
+ UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
+ // Press icon center until shortcuts appear
+ Point iconCenter = icon.getVisibleCenter();
+ sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
+ UiObject2 deepShortcutsContainer = findViewById(R.id.deep_shortcuts_container);
+ assertNotNull(deepShortcutsContainer);
+ sendPointer(MotionEvent.ACTION_UP, iconCenter);
+
+ // Drag the first shortcut to the home screen.
+ assertTrue(deepShortcutsContainer.getChildCount() > 0);
+ UiObject2 shortcut = deepShortcutsContainer.getChildren().get(0)
+ .findObject(getSelectorForId(R.id.deep_shortcut));
+ String shortcutName = shortcut.getText();
+ dragToWorkspace(shortcut, false);
+
+ // Verify that the shortcut works on home screen
+ // (the app opens and has the same text as the shortcut).
+ mDevice.findObject(By.text(shortcutName)).click();
+ assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+ mSettingsApp.getComponentName().getPackageName())
+ .text(shortcutName)), DEFAULT_UI_TIMEOUT));
+ }
+}