summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/AppInfo.java9
-rw-r--r--src/com/android/launcher3/AutoInstallsLayout.java72
-rw-r--r--src/com/android/launcher3/BubbleTextView.java4
-rw-r--r--src/com/android/launcher3/ButtonDropTarget.java17
-rw-r--r--src/com/android/launcher3/CellLayout.java18
-rw-r--r--src/com/android/launcher3/DefaultLayoutParser.java64
-rw-r--r--src/com/android/launcher3/DeleteDropTarget.java7
-rw-r--r--src/com/android/launcher3/DeviceProfile.java34
-rw-r--r--src/com/android/launcher3/DropTargetBar.java9
-rw-r--r--src/com/android/launcher3/ExtendedEditText.java30
-rw-r--r--src/com/android/launcher3/FolderInfo.java8
-rw-r--r--src/com/android/launcher3/HolographicOutlineHelper.java50
-rw-r--r--src/com/android/launcher3/Hotseat.java3
-rw-r--r--src/com/android/launcher3/IconCache.java3
-rw-r--r--src/com/android/launcher3/InfoDropTarget.java3
-rw-r--r--src/com/android/launcher3/InstallShortcutReceiver.java10
-rw-r--r--src/com/android/launcher3/ItemInfo.java22
-rw-r--r--src/com/android/launcher3/Launcher.java677
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetInfo.java18
-rw-r--r--src/com/android/launcher3/LauncherCallbacks.java12
-rw-r--r--src/com/android/launcher3/LauncherExterns.java2
-rw-r--r--src/com/android/launcher3/LauncherModel.java17
-rw-r--r--src/com/android/launcher3/LauncherProvider.java29
-rw-r--r--src/com/android/launcher3/PendingAddItemInfo.java5
-rw-r--r--src/com/android/launcher3/PinchToOverviewListener.java4
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java10
-rw-r--r--src/com/android/launcher3/Utilities.java34
-rw-r--r--src/com/android/launcher3/Workspace.java236
-rw-r--r--src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java66
-rw-r--r--src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java36
-rw-r--r--src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java6
-rw-r--r--src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java6
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java15
-rw-r--r--src/com/android/launcher3/allapps/AllAppsSearchBarController.java8
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java3
-rw-r--r--src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java82
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompat.java8
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatV16.java5
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatVL.java4
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatVM.java34
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatVN.java13
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatVNMr1.java34
-rw-r--r--src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java76
-rw-r--r--src/com/android/launcher3/dragndrop/DragController.java79
-rw-r--r--src/com/android/launcher3/dragndrop/DragDriver.java165
-rw-r--r--src/com/android/launcher3/dragndrop/DragLayer.java161
-rw-r--r--src/com/android/launcher3/dragndrop/DragOptions.java31
-rw-r--r--src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java79
-rw-r--r--src/com/android/launcher3/folder/Folder.java158
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java28
-rw-r--r--src/com/android/launcher3/folder/FolderPagedView.java2
-rw-r--r--src/com/android/launcher3/graphics/DragPreviewProvider.java18
-rw-r--r--src/com/android/launcher3/model/PackageItemInfo.java9
-rw-r--r--src/com/android/launcher3/model/WidgetItem.java13
-rw-r--r--src/com/android/launcher3/pageindicators/PageIndicatorDots.java5
-rw-r--r--src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java5
-rw-r--r--src/com/android/launcher3/provider/RestoreDbTask.java20
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutView.java33
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java66
-rw-r--r--src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java14
-rw-r--r--src/com/android/launcher3/testing/LauncherExtension.java27
-rw-r--r--src/com/android/launcher3/util/ActivityResultInfo.java70
-rw-r--r--src/com/android/launcher3/util/ComponentKey.java6
-rw-r--r--src/com/android/launcher3/util/PackageManagerHelper.java54
-rw-r--r--src/com/android/launcher3/util/PendingRequestArgs.java127
-rw-r--r--src/com/android/launcher3/util/Preconditions.java6
-rw-r--r--src/com/android/launcher3/util/VerticalFlingDetector.java88
-rw-r--r--src/com/android/launcher3/widget/PendingAddShortcutInfo.java6
-rw-r--r--src/com/android/launcher3/widget/PendingAddWidgetInfo.java6
-rw-r--r--src/com/android/launcher3/widget/PendingItemPreviewProvider.java76
-rw-r--r--src/com/android/launcher3/widget/WidgetCell.java2
-rw-r--r--src/com/android/launcher3/widget/WidgetHostViewLoader.java4
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java51
73 files changed, 1927 insertions, 1285 deletions
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index e0694f3cf..4c4d67c59 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -62,7 +62,7 @@ public class AppInfo extends ItemInfo {
*/
int isDisabled = ShortcutInfo.DEFAULT;
- AppInfo() {
+ public AppInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
@@ -125,11 +125,8 @@ public class AppInfo extends ItemInfo {
}
@Override
- public String toString() {
- return "ApplicationInfo(title=" + title + " id=" + this.id
- + " type=" + this.itemType + " container=" + this.container
- + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
- + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
+ protected String dumpProperties() {
+ return super.dumpProperties() + " componentName=" + componentName;
}
/**
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 0d5043bb2..d5309b4f0 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -316,7 +316,7 @@ public class AutoInstallsLayout {
parsers.put(TAG_APP_ICON, new AppShortcutParser());
parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
parsers.put(TAG_FOLDER, new FolderParser());
- parsers.put(TAG_APPWIDGET, new AppWidgetParser());
+ parsers.put(TAG_APPWIDGET, new PendingWidgetParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
return parsers;
}
@@ -459,8 +459,12 @@ public class AutoInstallsLayout {
/**
* AppWidget parser: Required attributes packageName, className, spanX and spanY.
* Options child nodes: <extra key=... value=... />
+ * It adds a pending widget which allows the widget to come later. If there are extras, those
+ * are passed to widget options during bind.
+ * The config activity for the widget (if present) is not shown, so any optional configurations
+ * should be passed as extras and the widget should support reading these widget options.
*/
- protected class AppWidgetParser implements TagParser {
+ protected class PendingWidgetParser implements TagParser {
@Override
public long parseAndAdd(XmlResourceParser parser)
@@ -468,27 +472,13 @@ public class AutoInstallsLayout {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
- if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+ if (LOGD) Log.d(TAG, "Skipping invalid <appwidget> with no component");
return -1;
}
- ComponentName cn = new ComponentName(packageName, className);
- try {
- mPackageManager.getReceiverInfo(cn, 0);
- } catch (Exception e) {
- String[] packages = mPackageManager.currentToCanonicalPackageNames(
- new String[] { packageName });
- cn = new ComponentName(packages[0], className);
- try {
- mPackageManager.getReceiverInfo(cn, 0);
- } catch (Exception e1) {
- if (LOGD) Log.d(TAG, "Can't find widget provider: " + className);
- return -1;
- }
- }
-
mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X));
mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y));
+ mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
// Read the extras
Bundle extras = new Bundle();
@@ -513,38 +503,26 @@ public class AutoInstallsLayout {
}
}
- final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
- long insertedId = -1;
- try {
- int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-
- if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
- if (LOGD) Log.e(TAG, "Unable to bind app widget id " + cn);
- return -1;
- }
+ return verifyAndInsert(new ComponentName(packageName, className), extras);
+ }
- mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
- mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
- mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
- mValues.put(Favorites._ID, mCallback.generateNewItemId());
- insertedId = mCallback.insertAndCheck(mDb, mValues);
- if (insertedId < 0) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
- return insertedId;
- }
+ protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+ mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+ mValues.put(Favorites.RESTORED,
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+ LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+ mValues.put(Favorites._ID, mCallback.generateNewItemId());
+ if (!extras.isEmpty()) {
+ mValues.put(Favorites.INTENT, new Intent().putExtras(extras).toUri(0));
+ }
- // Send a broadcast to configure the widget
- if (!extras.isEmpty()) {
- Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
- intent.setComponent(cn);
- intent.putExtras(extras);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- mContext.sendBroadcast(intent);
- }
- } catch (RuntimeException ex) {
- if (LOGD) Log.e(TAG, "Problem allocating appWidgetId", ex);
+ long insertedId = mCallback.insertAndCheck(mDb, mValues);
+ if (insertedId < 0) {
+ return -1;
+ } else {
+ return insertedId;
}
- return insertedId;
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 33b3ad347..a294fa538 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -65,6 +65,7 @@ public class BubbleTextView extends TextView
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
+ private static final int DISPLAY_FOLDER = 2;
private final Launcher mLauncher;
private Drawable mIcon;
@@ -125,12 +126,13 @@ public class BubbleTextView extends TextView
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
+ } else if (display == DISPLAY_FOLDER) {
+ setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
}
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
defaultIconSize);
-
a.recycle();
if (mCustomShadowsEnabled) {
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 0f6073eee..cf8abae2e 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -43,6 +43,7 @@ import android.widget.TextView;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.util.Thunk;
@@ -199,8 +200,8 @@ public abstract class ButtonDropTarget extends TextView
}
@Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
- mActive = supportsDrop(source, info);
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ mActive = supportsDrop(dragObject.dragSource, dragObject.dragInfo);
mDrawable.setColorFilter(null);
if (mCurrentColorAnim != null) {
mCurrentColorAnim.cancel();
@@ -209,6 +210,9 @@ public abstract class ButtonDropTarget extends TextView
setTextColor(mOriginalTextColor);
(mHideParentOnDisable ? ((ViewGroup) getParent()) : this)
.setVisibility(mActive ? View.VISIBLE : View.GONE);
+
+ mAccessibleDrag = options.isAccessibleDrag;
+ setOnClickListener(mAccessibleDrag ? this : null);
}
@Override
@@ -227,6 +231,7 @@ public abstract class ButtonDropTarget extends TextView
@Override
public void onDragEnd() {
mActive = false;
+ setOnClickListener(null);
}
/**
@@ -254,7 +259,8 @@ public abstract class ButtonDropTarget extends TextView
}
};
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
- DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
+ mLauncher.getDragController().isExternalDrag() ? 1 : DRAG_VIEW_DROP_DURATION,
+ new DecelerateInterpolator(2),
new LinearInterpolator(), onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
@@ -308,11 +314,6 @@ public abstract class ButtonDropTarget extends TextView
return to;
}
- public void enableAccessibleDrag(boolean enable) {
- mAccessibleDrag = enable;
- setOnClickListener(enable ? this : null);
- }
-
@Override
public void onClick(View v) {
mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 77f6612c1..6714d9f17 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -54,6 +54,7 @@ import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
@@ -235,6 +236,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new Rect(-1, -1, -1, -1);
}
+ mDragOutlinePaint.setColor(getResources().getColor(R.color.outline_color));
// When dragging things around the home screens, we show a green outline of
// where the item will land. The outlines gradually fade out, leaving a trail
@@ -1047,15 +1049,16 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
return false;
}
- void visualizeDropLocation(View v, Bitmap dragOutline, int cellX, int cellY, int spanX,
- int spanY, boolean resize, DropTarget.DragObject dragObject) {
+ void visualizeDropLocation(View v, DragPreviewProvider outlineProvider, int cellX, int cellY,
+ int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
final int oldDragCellX = mDragCell[0];
final int oldDragCellY = mDragCell[1];
- if (dragOutline == null && v == null) {
+ if (outlineProvider == null || outlineProvider.gerenatedDragOutline == null) {
return;
}
+ Bitmap dragOutline = outlineProvider.gerenatedDragOutline;
if (cellX != oldDragCellX || cellY != oldDragCellY) {
Point dragOffset = dragObject.dragView.getDragVisualizeOffset();
Rect dragRegion = dragObject.dragView.getDragRegion();
@@ -1354,12 +1357,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// and that passed in.
int curDirectionScore = direction[0] * curDirection[0] +
direction[1] * curDirection[1];
- boolean exactDirectionOnly = false;
- boolean directionMatches = direction[0] == curDirection[0] &&
- direction[0] == curDirection[0];
- if ((directionMatches || !exactDirectionOnly) &&
- Float.compare(distance, bestDistance) < 0 || (Float.compare(distance,
- bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
+ if (Float.compare(distance, bestDistance) < 0 ||
+ (Float.compare(distance, bestDistance) == 0
+ && curDirectionScore > bestDirectionScore)) {
bestDistance = distance;
bestDirectionScore = curDirectionScore;
bestXY[0] = x;
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index e6f34d952..ef28d1e8c 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -1,6 +1,8 @@
package com.android.launcher3;
import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -9,6 +11,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
@@ -42,6 +45,10 @@ public class DefaultLayoutParser extends AutoInstallsLayout {
private static final String ATTR_SCREEN = "screen";
private static final String ATTR_FOLDER_ITEMS = "folderItems";
+ // TODO: Remove support for this broadcast, instead use widget options to send bind time options
+ private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
+ "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
+
public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, Resources sourceRes, int layoutId) {
super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
@@ -270,4 +277,61 @@ public class DefaultLayoutParser extends AutoInstallsLayout {
return super.parseAndAdd(parser);
}
}
+
+
+ /**
+ * AppWidget parser which enforces that the app is already installed when the layout is parsed.
+ */
+ protected class AppWidgetParser extends PendingWidgetParser {
+
+ @Override
+ protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+ try {
+ mPackageManager.getReceiverInfo(cn, 0);
+ } catch (Exception e) {
+ String[] packages = mPackageManager.currentToCanonicalPackageNames(
+ new String[] { cn.getPackageName() });
+ cn = new ComponentName(packages[0], cn.getClassName());
+ try {
+ mPackageManager.getReceiverInfo(cn, 0);
+ } catch (Exception e1) {
+ Log.d(TAG, "Can't find widget provider: " + cn.getClassName());
+ return -1;
+ }
+ }
+
+ final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+ long insertedId = -1;
+ try {
+ int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
+ Log.e(TAG, "Unable to bind app widget id " + cn);
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ return -1;
+ }
+
+ mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
+ mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+ mValues.put(Favorites._ID, mCallback.generateNewItemId());
+ insertedId = mCallback.insertAndCheck(mDb, mValues);
+ if (insertedId < 0) {
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ return insertedId;
+ }
+
+ // Send a broadcast to configure the widget
+ if (!extras.isEmpty()) {
+ Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
+ intent.setComponent(cn);
+ intent.putExtras(extras);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ mContext.sendBroadcast(intent);
+ }
+ } catch (RuntimeException ex) {
+ Log.e(TAG, "Problem allocating appWidgetId", ex);
+ }
+ return insertedId;
+ }
+ }
}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index f24e00b5c..705f84101 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -25,6 +25,7 @@ import android.view.View;
import android.view.animation.AnimationUtils;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.util.FlingAnimation;
import com.android.launcher3.util.Thunk;
@@ -49,9 +50,9 @@ public class DeleteDropTarget extends ButtonDropTarget {
}
@Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
- super.onDragStart(source, info, dragAction);
- setTextBasedOnDragSource(source);
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ super.onDragStart(dragObject, options);
+ setTextBasedOnDragSource(dragObject.dragSource);
}
/** @return true for items that should have a "Remove" action in accessibility. */
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c9fd85a16..c4e6ed119 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -20,8 +20,6 @@ import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetrics;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.DisplayMetrics;
@@ -100,6 +98,7 @@ public class DeviceProfile {
public int folderIconPreviewPadding;
public int folderCellWidthPx;
public int folderCellHeightPx;
+ public int folderChildDrawablePaddingPx;
// Hotseat
public int hotseatCellWidthPx;
@@ -255,12 +254,9 @@ public class DeviceProfile {
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
allAppsIconTextSizePx = iconTextSizePx;
- // Calculate the actual text height
- Paint textPaint = new Paint();
- textPaint.setTextSize(iconTextSizePx);
- FontMetrics fm = textPaint.getFontMetrics();
cellWidthPx = iconSizePx;
- cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
+ cellHeightPx = iconSizePx + iconDrawablePaddingPx
+ + Utilities.calculateTextHeight(iconTextSizePx);
final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
: res.getDimensionPixelSize(R.dimen.dragViewScale);
dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
@@ -281,12 +277,25 @@ public class DeviceProfile {
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
}
- // Folder
- int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
+ // Folder cell
+ int cellPaddingX = res.getDimensionPixelSize(R.dimen.folder_cell_x_padding);
+ int cellPaddingY = res.getDimensionPixelSize(R.dimen.folder_cell_y_padding);
+ final int folderChildTextSize =
+ Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_child_text_size));
+
+ final int folderBottomPanelSize =
+ 2 * res.getDimensionPixelSize(R.dimen.folder_label_padding)
+ + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
+
// Don't let the folder get too close to the edges of the screen.
- folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
+ folderCellWidthPx = Math.min(iconSizePx + 2 * cellPaddingX,
(availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
- folderCellHeightPx = cellHeightPx + edgeMarginPx;
+ folderCellHeightPx = Math.min(iconSizePx + 3 * cellPaddingY + folderChildTextSize,
+ (availableHeightPx - 4 * edgeMarginPx - folderBottomPanelSize) / inv.numFolderRows);
+ folderChildDrawablePaddingPx = Math.max(0,
+ (folderCellHeightPx - iconSizePx - folderChildTextSize) / 3);
+
+ // Folder icon
folderBackgroundOffset = -edgeMarginPx;
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
@@ -296,9 +305,6 @@ public class DeviceProfile {
mInsets.set(insets);
}
- /**
- * @param recyclerViewWidth the available width of the AllAppsRecyclerView
- */
public void updateAppsViewNumCols() {
allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns;
}
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 5966af51c..42bab4705 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -27,6 +27,7 @@ import android.view.animation.AccelerateInterpolator;
import android.widget.LinearLayout;
import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
/*
* The top bar containing various drop targets: Delete/App Info/Uninstall.
@@ -120,17 +121,11 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi
}
}
- public void enableAccessibleDrag(boolean enable) {
- mDeleteDropTarget.enableAccessibleDrag(enable);
- mAppInfoDropTarget.enableAccessibleDrag(enable);
- mUninstallDropTarget.enableAccessibleDrag(enable);
- }
-
/*
* DragController.DragListener implementation
*/
@Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
animateToVisibility(true);
}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index bf4551b26..c06f727a5 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -19,6 +19,7 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.DragEvent;
import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
@@ -27,6 +28,8 @@ import android.widget.EditText;
*/
public class ExtendedEditText extends EditText {
+ private boolean mShowImeAfterFirstLayout;
+
/**
* Implemented by listeners of the back key.
*/
@@ -37,10 +40,12 @@ public class ExtendedEditText extends EditText {
private OnBackKeyListener mBackKeyListener;
public ExtendedEditText(Context context) {
+ // ctor chaining breaks the touch handling
super(context);
}
public ExtendedEditText(Context context, AttributeSet attrs) {
+ // ctor chaining breaks the touch handling
super(context, attrs);
}
@@ -69,4 +74,29 @@ public class ExtendedEditText extends EditText {
// We don't want this view to interfere with Launcher own drag and drop.
return false;
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mShowImeAfterFirstLayout) {
+ // soft input only shows one frame after the layout of the EditText happens,
+ post(new Runnable() {
+ @Override
+ public void run() {
+ showSoftInput();
+ mShowImeAfterFirstLayout = false;
+ }
+ });
+ }
+ }
+
+ public void showKeyboard() {
+ mShowImeAfterFirstLayout = !showSoftInput();
+ }
+
+ private boolean showSoftInput() {
+ return requestFocus() &&
+ ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
+ .showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);
+ }
}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 9a9985211..c0a8caaa3 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -126,14 +126,6 @@ public class FolderInfo extends ItemInfo {
public void onItemsChanged(boolean animate);
}
- @Override
- public String toString() {
- return "FolderInfo(id=" + this.id + " type=" + this.itemType
- + " container=" + this.container + " screen=" + screenId
- + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + ")";
- }
-
public boolean hasOption(int optionFlag) {
return (options & optionFlag) != 0;
}
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 427acea76..9dec7d9e4 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -29,6 +29,10 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
+import com.android.launcher3.config.ProviderConfig;
+
+import java.nio.ByteBuffer;
+
/**
* Utility class to generate shadow and outline effect, which are used for click feedback
* and drag-n-drop respectively.
@@ -79,50 +83,53 @@ public class HolographicOutlineHelper {
* Applies a more expensive and accurate outline to whatever is currently drawn in a specified
* bitmap.
*/
- public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor) {
- applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
+ public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
+ applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, true);
}
- void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor, boolean clipAlpha) {
+ public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas,
+ boolean clipAlpha) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
+ throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
+ }
// We start by removing most of the alpha channel so as to ignore shadows, and
// other types of partial transparency when defining the shape of the object
if (clipAlpha) {
- int[] srcBuffer = new int[srcDst.getWidth() * srcDst.getHeight()];
- srcDst.getPixels(srcBuffer,
- 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
- for (int i = 0; i < srcBuffer.length; i++) {
- final int alpha = srcBuffer[i] >>> 24;
- if (alpha < 188) {
- srcBuffer[i] = 0;
+ byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()];
+ ByteBuffer buffer = ByteBuffer.wrap(pixels);
+ buffer.rewind();
+ srcDst.copyPixelsToBuffer(buffer);
+
+ for (int i = 0; i < pixels.length; i++) {
+ if ((pixels[i] & 0xFF) < 188) {
+ pixels[i] = 0;
}
}
- srcDst.setPixels(srcBuffer,
- 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
+
+ buffer.rewind();
+ srcDst.copyPixelsFromBuffer(buffer);
}
- Bitmap glowShape = srcDst.extractAlpha();
// calculate the outer blur first
mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
int[] outerBlurOffset = new int[2];
- Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
+ Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset);
mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
int[] brightOutlineOffset = new int[2];
- Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
+ Bitmap brightOutline = srcDst.extractAlpha(mBlurPaint, brightOutlineOffset);
// calculate the inner blur
- srcDstCanvas.setBitmap(glowShape);
+ srcDstCanvas.setBitmap(srcDst);
srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
int[] thickInnerBlurOffset = new int[2];
- Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
+ Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset);
// mask out the inner blur
srcDstCanvas.setBitmap(thickInnerBlur);
- srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
+ srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
-thickInnerBlurOffset[1], mErasePaint);
srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
mErasePaint);
@@ -132,14 +139,12 @@ public class HolographicOutlineHelper {
// draw the inner and outer blur
srcDstCanvas.setBitmap(srcDst);
srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- mDrawPaint.setColor(color);
srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
mDrawPaint);
srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
mDrawPaint);
// draw the bright outline
- mDrawPaint.setColor(outlineColor);
srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
mDrawPaint);
@@ -148,7 +153,6 @@ public class HolographicOutlineHelper {
brightOutline.recycle();
thickOuterBlur.recycle();
thickInnerBlur.recycle();
- glowShape.recycle();
}
Bitmap createMediumDropShadow(BubbleTextView view) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index c738480fe..f9424d483 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -25,6 +25,7 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -69,7 +70,7 @@ public class Hotseat extends FrameLayout
mLauncher = (Launcher) context;
mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
mBackgroundColor = ColorUtils.setAlphaComponent(
- context.getColor(R.color.all_apps_container_color), 0);
+ ContextCompat.getColor(context, R.color.all_apps_container_color), 0);
mBackground = new ColorDrawable(mBackgroundColor);
setBackground(mBackground);
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index a49162c8b..d3fb38ede 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -853,8 +853,7 @@ public class IconCache {
values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
values.put(IconDB.COLUMN_LABEL, label);
- values.put(IconDB.COLUMN_SYSTEM_STATE,
- mIconProvider.getIconSystemState(mIconProvider.getIconSystemState(packageName)));
+ values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
return values;
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index e136bcd99..398c9d2a2 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -100,6 +100,7 @@ public class InfoDropTarget extends UninstallDropTarget {
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
return developmentSettingsEnabled
&& (info instanceof AppInfo || info instanceof ShortcutInfo
- || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo);
+ || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo)
+ && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df87cc204..d8e58d829 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -33,6 +33,7 @@ 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.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
import org.json.JSONException;
@@ -146,6 +147,15 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
PendingInstallShortcutInfo info = createPendingInfo(context, data);
if (info != null) {
+ if (!info.isLauncherActivity()) {
+ // Since its a custom shortcut, verify that it is safe to launch.
+ if (!PackageManagerHelper.hasPermissionForActivity(
+ context, info.launchIntent, null)) {
+ // Target cannot be launched, or requires some special permission to launch
+ Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
+ return;
+ }
+ }
queuePendingShortcutInfo(info, context);
}
}
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 2a94e55c0..c0c22a325 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -189,10 +189,24 @@ public class ItemInfo {
}
@Override
- public String toString() {
- return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
- + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + " user=" + user + ")";
+ public final String toString() {
+ return getClass().getSimpleName() + "(" + dumpProperties() + ")";
+ }
+
+ protected String dumpProperties() {
+ return "id=" + id
+ + " type=" + itemType
+ + " container=" + container
+ + " screen=" + screenId
+ + " cellX=" + cellX
+ + " cellY=" + cellY
+ + " spanX=" + spanX
+ + " spanY=" + spanY
+ + " minSpanX=" + minSpanX
+ + " minSpanY=" + minSpanY
+ + " rank=" + rank
+ + " user=" + user
+ + " title=" + title;
}
/**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1caec339b..b6474e6ef 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -25,7 +25,6 @@ import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.SearchManager;
@@ -36,7 +35,6 @@ 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.ContextWrapper;
import android.content.DialogInterface;
@@ -51,10 +49,8 @@ import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
@@ -81,7 +77,6 @@ import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.OvershootInterpolator;
@@ -106,6 +101,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.folder.Folder;
@@ -119,10 +115,12 @@ import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -164,10 +162,6 @@ public class Launcher extends Activity
private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
- private static final int WORKSPACE_BACKGROUND_GRADIENT = 0;
- private static final int WORKSPACE_BACKGROUND_TRANSPARENT = 1;
- private static final int WORKSPACE_BACKGROUND_BLACK = 2;
-
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
/**
@@ -191,15 +185,11 @@ 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: 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";
-
- static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
- static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
+ // Type: PendingRequestArgs
+ private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
+ // Type: ActivityResultInfo
+ private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
+
static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
/** The different states that Launcher can be in. */
@@ -246,10 +236,6 @@ public class Launcher extends Activity
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
- @Thunk final ItemInfo mPendingAddInfo = new ItemInfo();
- private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
- private int mPendingAddWidgetId = -1;
-
private int[] mTmpAddItemCellCoordinates = new int[2];
@Thunk Hotseat mHotseat;
@@ -279,8 +265,6 @@ public class Launcher extends Activity
@Thunk boolean mWorkspaceLoading = true;
private boolean mPaused = true;
- private boolean mRestoring;
- private boolean mWaitingForResult;
private boolean mOnResumeNeedsLoad;
private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
@@ -316,10 +300,7 @@ public class Launcher extends Activity
// match the sensor state.
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
- @Thunk Drawable mWorkspaceBackgroundDrawable;
-
private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
- private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -363,17 +344,13 @@ public class Launcher extends Activity
}
};
- private static PendingAddArguments sPendingAddItem;
-
- @Thunk static class PendingAddArguments {
- int requestCode;
- Intent intent;
- long container;
- long screenId;
- int cellX;
- int cellY;
- int appWidgetId;
- }
+ // Activity result which needs to be processed after workspace has loaded.
+ private ActivityResultInfo mPendingActivityResult;
+ /**
+ * Holds extra information required to handle a result from an external call, like
+ * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)}
+ */
+ private PendingRequestArgs mPendingRequestArgs;
private UserEventDispatcher mUserEventDispatcher;
@@ -464,20 +441,14 @@ public class Launcher extends Activity
Trace.endSection();
}
- if (!mRestoring) {
- if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
- // If the user leaves launcher, then we should just load items asynchronously when
- // they return.
- mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
- } else {
- // We only load the page synchronously if the user rotates (or triggers a
- // configuration change) while launcher is in the foreground
- if (!mModel.startLoader(mWorkspace.getRestorePage())) {
- // If we are not binding synchronously, show a fade in animation when
- // the first page bind completes.
- mDragLayer.setAlpha(0);
- }
- }
+ // We only load the page synchronously if the user rotates (or triggers a
+ // configuration change) while launcher is in the foreground
+ if (!mModel.startLoader(mWorkspace.getRestorePage())) {
+ // If we are not binding synchronously, show a fade in animation when
+ // the first page bind completes.
+ mDragLayer.setAlpha(0);
+ } else {
+ setWorkspaceLoading(true);
}
// For handling default keys
@@ -504,12 +475,6 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
-
- if (shouldShowIntroScreen()) {
- showIntroScreen();
- } else {
- showFirstRunActivity();
- }
}
@Override
@@ -676,53 +641,61 @@ public class Launcher extends Activity
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
*/
- private long completeAdd(PendingAddArguments args) {
- long screenId = args.screenId;
- if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ private long completeAdd(
+ int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) {
+ long screenId = info.screenId;
+ if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
// When the screen id represents an actual screen (as opposed to a rank) we make sure
// that the drop page actually exists.
- screenId = ensurePendingDropLayoutExists(args.screenId);
+ screenId = ensurePendingDropLayoutExists(info.screenId);
}
- switch (args.requestCode) {
+ switch (requestCode) {
case REQUEST_CREATE_SHORTCUT:
- completeAddShortcut(args.intent, args.container, screenId, args.cellX,
- args.cellY);
+ completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info);
break;
case REQUEST_CREATE_APPWIDGET:
- completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
+ completeAddAppWidget(appWidgetId, info, null, null);
break;
case REQUEST_RECONFIGURE_APPWIDGET:
- completeRestoreAppWidget(args.appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
+ completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
break;
case REQUEST_BIND_PENDING_APPWIDGET: {
- int widgetId = args.appWidgetId;
- LauncherAppWidgetInfo info =
+ int widgetId = appWidgetId;
+ LauncherAppWidgetInfo widgetInfo =
completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
- if (info != null) {
+ if (widgetInfo != null) {
// Since the view was just bound, also launch the configure activity if needed
LauncherAppWidgetProviderInfo provider = mAppWidgetManager
.getLauncherAppWidgetInfo(widgetId);
if (provider != null && provider.configure != null) {
- startRestoredWidgetReconfigActivity(provider, info);
+ startRestoredWidgetReconfigActivity(provider, widgetInfo);
}
}
break;
}
}
- // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
- // if you turned the screen off and then back while in All Apps, Launcher would not
- // return to the workspace. Clearing mAddInfo.container here fixes this issue
- resetAddInfo();
+
return screenId;
}
private void handleActivityResult(
final int requestCode, final int resultCode, final Intent data) {
+ if (isWorkspaceLoading()) {
+ // process the result once the workspace has loaded.
+ mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data);
+ return;
+ }
+ mPendingActivityResult = null;
+
// Reset the startActivity waiting flag
- setWaitingForResult(false);
- final int pendingAddWidgetId = mPendingAddWidgetId;
- mPendingAddWidgetId = -1;
+ final PendingRequestArgs requestArgs = mPendingRequestArgs;
+ setWaitingForResult(null);
+ if (requestArgs == null) {
+ return;
+ }
+
+ final int pendingAddWidgetId = requestArgs.getWidgetId();
Runnable exitSpringLoaded = new Runnable() {
@Override
@@ -737,12 +710,14 @@ public class Launcher extends Activity
final int appWidgetId = data != null ?
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
- completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
+ completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs);
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_OK) {
- addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
- mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
+ addAppWidgetImpl(
+ appWidgetId, requestArgs, null,
+ requestArgs.getWidgetProvider(),
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY);
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -758,7 +733,6 @@ public class Launcher extends Activity
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
requestCode == REQUEST_CREATE_APPWIDGET);
- final boolean workspaceLocked = isWorkspaceLocked();
// We have special handling for widgets
if (isWidgetDrop) {
final int appWidgetId;
@@ -775,46 +749,36 @@ public class Launcher extends Activity
Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
"returned from the widget configuration activity.");
result = RESULT_CANCELED;
- completeTwoStageWidgetDrop(result, appWidgetId);
+ completeTwoStageWidgetDrop(result, appWidgetId, requestArgs);
final Runnable onComplete = new Runnable() {
@Override
public void run() {
exitSpringLoadedDragModeDelayed(false, 0, null);
}
};
- if (workspaceLocked) {
- // No need to remove the empty screen if we're mid-binding, as the
- // the bind will not add the empty screen.
- mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
- } else {
- mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
- }
- } else {
- if (!workspaceLocked) {
- if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- // When the screen id represents an actual screen (as opposed to a rank)
- // we make sure that the drop page actually exists.
- mPendingAddInfo.screenId =
- ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
- }
- final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
- dropLayout.setDropPending(true);
- final Runnable onComplete = new Runnable() {
- @Override
- public void run() {
- completeTwoStageWidgetDrop(resultCode, appWidgetId);
- dropLayout.setDropPending(false);
- }
- };
- mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
- } else {
- PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
- mPendingAddInfo);
- sPendingAddItem = args;
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ } else {
+ if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ // When the screen id represents an actual screen (as opposed to a rank)
+ // we make sure that the drop page actually exists.
+ requestArgs.screenId =
+ ensurePendingDropLayoutExists(requestArgs.screenId);
}
+ final CellLayout dropLayout =
+ mWorkspace.getScreenWithId(requestArgs.screenId);
+
+ dropLayout.setDropPending(true);
+ final Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs);
+ dropLayout.setDropPending(false);
+ }
+ };
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
return;
}
@@ -823,13 +787,7 @@ public class Launcher extends Activity
|| requestCode == REQUEST_BIND_PENDING_APPWIDGET) {
if (resultCode == RESULT_OK) {
// Update the widget view.
- PendingAddArguments args = preparePendingAddArgs(requestCode, data,
- pendingAddWidgetId, mPendingAddInfo);
- if (workspaceLocked) {
- sPendingAddItem = args;
- } else {
- completeAdd(args);
- }
+ completeAdd(requestCode, data, pendingAddWidgetId, requestArgs);
}
// Leave the widget in the pending state if the user canceled the configure.
return;
@@ -837,23 +795,17 @@ public class Launcher extends Activity
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);
- }
+ if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) {
+ completeAdd(requestCode, data, -1, requestArgs);
+ 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();
-
}
@Override
@@ -868,22 +820,25 @@ public class Launcher extends Activity
/** @Override for MNC */
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
- if (requestCode == REQUEST_PERMISSION_CALL_PHONE && sPendingAddItem != null
- && sPendingAddItem.requestCode == REQUEST_PERMISSION_CALL_PHONE) {
+ PendingRequestArgs pendingArgs = mPendingRequestArgs;
+ if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null
+ && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) {
+ setWaitingForResult(null);
+
View v = null;
- CellLayout layout = getCellLayout(sPendingAddItem.container, sPendingAddItem.screenId);
+ CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId);
if (layout != null) {
- v = layout.getChildAt(sPendingAddItem.cellX, sPendingAddItem.cellY);
+ v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY);
}
- Intent intent = sPendingAddItem.intent;
- sPendingAddItem = null;
+ Intent intent = pendingArgs.getPendingIntent();
+
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startActivitySafely(v, intent, null);
} else {
// TODO: Show a snack bar with link to settings
Toast.makeText(this, getString(R.string.msg_no_phone_permission,
- getString(R.string.app_name)), Toast.LENGTH_SHORT).show();
+ getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
}
}
if (mLauncherCallbacks != null) {
@@ -892,19 +847,6 @@ public class Launcher extends Activity
}
}
- private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
- appWidgetId, ItemInfo info) {
- PendingAddArguments args = new PendingAddArguments();
- args.requestCode = requestCode;
- args.intent = data;
- args.container = info.container;
- args.screenId = info.screenId;
- args.cellX = info.cellX;
- args.cellY = info.cellY;
- args.appWidgetId = appWidgetId;
- return args;
- }
-
/**
* Check to see if a given screen id exists. If not, create it at the end, return the new id.
*
@@ -923,8 +865,9 @@ public class Launcher extends Activity
}
}
- @Thunk void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
- CellLayout cellLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+ @Thunk void completeTwoStageWidgetDrop(
+ final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) {
+ CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId);
Runnable onCompleteRunnable = null;
int animationType = 0;
@@ -932,13 +875,12 @@ public class Launcher extends Activity
if (resultCode == RESULT_OK) {
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
- mPendingAddWidgetInfo);
+ requestArgs.getWidgetProvider());
boundWidget = layout;
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
- mPendingAddInfo.screenId, layout, null);
+ completeAddAppWidget(appWidgetId, requestArgs, layout, null);
exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
}
@@ -948,7 +890,7 @@ public class Launcher extends Activity
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
}
if (mDragLayer.getAnimatedView() != null) {
- mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
+ mWorkspace.animateWidgetDrop(requestArgs, cellLayout,
(DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
animationType, boundWidget, true);
} else if (onCompleteRunnable != null) {
@@ -1009,21 +951,16 @@ public class Launcher extends Activity
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
- false /* focusSearchBar */);
+ mAppsView.shouldRestoreImeState() /* focusSearchBar */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
mOnResumeState = State.NONE;
- // Background was set to gradient in onPause(), restore to transparent if in all apps.
- setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT
- : WORKSPACE_BACKGROUND_TRANSPARENT);
-
mPaused = false;
- if (mRestoring || mOnResumeNeedsLoad) {
+ if (mOnResumeNeedsLoad) {
setWorkspaceLoading(true);
mModel.startLoader(getCurrentWorkspaceScreen());
- mRestoring = false;
mOnResumeNeedsLoad = false;
}
if (mBindOnResumeCallbacks.size() > 0) {
@@ -1295,7 +1232,8 @@ public class Launcher extends Activity
return mDefaultKeySsb.toString();
}
- private void clearTypedText() {
+ @Override
+ public void clearTypedText() {
mDefaultKeySsb.clear();
mDefaultKeySsb.clearSpans();
Selection.setSelection(mDefaultKeySsb, 0);
@@ -1338,18 +1276,12 @@ public class Launcher extends Activity
mWorkspace.setRestorePage(currentScreen);
}
- 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 ?
- null : LauncherAppWidgetProviderInfo.fromProviderInfo(this, info);
-
- mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
- setWaitingForResult(true);
- mRestoring = true;
+ PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
+ if (requestArgs != null) {
+ setWaitingForResult(requestArgs);
}
+
+ mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
}
/**
@@ -1367,10 +1299,8 @@ public class Launcher extends Activity
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
// Setup the drag layer
-
mDragLayer.setup(this, mDragController, mAllAppsController);
// Setup the hotseat
@@ -1527,12 +1457,19 @@ public class Launcher extends Activity
* @param data The intent describing the shortcut.
*/
private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
- int cellY) {
+ int cellY, PendingRequestArgs args) {
int[] cellXY = mTmpAddItemCellCoordinates;
CellLayout layout = getCellLayout(container, screenId);
ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
- if (info == null) {
+ if (info == null || args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
+ args.getPendingIntent().getComponent() == null) {
+ return;
+ }
+ if (!PackageManagerHelper.hasPermissionForActivity(
+ this, info.intent, args.getPendingIntent().getComponent().getPackageName())) {
+ // The app is trying to add a shortcut without sufficient permissions
+ Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
return;
}
final View view = createShortcut(info);
@@ -1566,10 +1503,8 @@ public class Launcher extends Activity
LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]);
- if (!mRestoring) {
- mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
- isWorkspaceLocked());
- }
+ mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
+ isWorkspaceLocked());
}
/**
@@ -1577,10 +1512,9 @@ public class Launcher extends Activity
*
* @param appWidgetId The app widget id
*/
- @Thunk void completeAddAppWidget(int appWidgetId, long container, long screenId,
+ @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
- ItemInfo info = mPendingAddInfo;
if (appWidgetInfo == null) {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
}
@@ -1591,24 +1525,21 @@ public class Launcher extends Activity
LauncherAppWidgetInfo launcherInfo;
launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
- launcherInfo.spanX = info.spanX;
- launcherInfo.spanY = info.spanY;
- launcherInfo.minSpanX = info.minSpanX;
- launcherInfo.minSpanY = info.minSpanY;
+ launcherInfo.spanX = itemInfo.spanX;
+ launcherInfo.spanY = itemInfo.spanY;
+ launcherInfo.minSpanX = itemInfo.minSpanX;
+ launcherInfo.minSpanY = itemInfo.minSpanY;
launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
LauncherModel.addItemToDatabase(this, launcherInfo,
- container, screenId, info.cellX, info.cellY);
+ itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
- if (!mRestoring) {
- if (hostView == null) {
- // Perform actual inflation because we're live
- hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
- }
- hostView.setVisibility(View.VISIBLE);
- addAppWidgetToWorkspace(hostView, launcherInfo, appWidgetInfo, isWorkspaceLocked());
+ if (hostView == null) {
+ // Perform actual inflation because we're live
+ hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
}
- resetAddInfo();
+ hostView.setVisibility(View.VISIBLE);
+ addAppWidgetToWorkspace(hostView, launcherInfo, appWidgetInfo, isWorkspaceLocked());
}
private void addAppWidgetToWorkspace(
@@ -1639,8 +1570,7 @@ public class Launcher extends Activity
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
- if (mAppsView != null && mWidgetsView != null &&
- mPendingAddInfo.container == ItemInfo.NO_ID) {
+ if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) {
if (!showWorkspace(false)) {
// If we are already on the workspace, then manually reset all apps
mAppsView.reset();
@@ -1852,7 +1782,7 @@ public class Launcher extends Activity
getWindow().closeAllPanels();
// Whatever we were doing is hereby canceled.
- setWaitingForResult(false);
+ setWaitingForResult(null);
}
@Override
@@ -1970,13 +1900,11 @@ public class Launcher extends Activity
closeFolder(false);
closeShortcutsContainer(false);
- if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
- mWaitingForResult) {
- 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);
+ if (mPendingRequestArgs != null) {
+ outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
+ }
+ if (mPendingActivityResult != null) {
+ outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
}
if (mLauncherCallbacks != null) {
@@ -2039,14 +1967,12 @@ public class Launcher extends Activity
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- onStartForResult(requestCode);
super.startActivityForResult(intent, requestCode, options);
}
@Override
public void startIntentSenderForResult (IntentSender intent, int requestCode,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
- onStartForResult(requestCode);
try {
super.startIntentSenderForResult(intent, requestCode,
fillInIntent, flagsMask, flagsValues, extraFlags, options);
@@ -2055,12 +1981,6 @@ public class Launcher extends Activity
}
}
- private void onStartForResult(int requestCode) {
- if (requestCode >= 0) {
- setWaitingForResult(true);
- }
- }
-
/**
* Indicates that we want global search for this activity by setting the globalSearch
* argument for {@link #startSearch} to true.
@@ -2078,13 +1998,10 @@ public class Launcher extends Activity
appSearchData.putString("source", "launcher-search");
}
- // TODO send proper bounds.
- Rect sourceBounds = null;
-
- boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
- appSearchData, sourceBounds);
- if (clearTextImmediately) {
- clearTypedText();
+ if (mLauncherCallbacks == null ||
+ !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) {
+ // Starting search from the callbacks failed. Start the default global search.
+ startGlobalSearch(initialQuery, selectInitialQuery, appSearchData, null);
}
// We need to show the workspace after starting the search
@@ -2092,28 +2009,9 @@ public class Launcher extends Activity
}
/**
- * Start a text search.
- *
- * @return {@code true} if the search will start immediately, so any further keypresses
- * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
- * to buffer keypresses.
- */
- public boolean startSearch(String initialQuery,
- boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
- if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) {
- return mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData,
- sourceBounds);
- }
-
- startGlobalSearch(initialQuery, selectInitialQuery,
- appSearchData, sourceBounds);
- return false;
- }
-
- /**
* Starts the global search activity. This code is a copied from SearchManager
*/
- private void startGlobalSearch(String initialQuery,
+ public void startGlobalSearch(String initialQuery,
boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
final SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
@@ -2171,7 +2069,7 @@ public class Launcher extends Activity
}
public boolean isWorkspaceLocked() {
- return mWorkspaceLoading || mWaitingForResult;
+ return mWorkspaceLoading || mPendingRequestArgs != null;
}
public boolean isWorkspaceLoading() {
@@ -2186,9 +2084,9 @@ public class Launcher extends Activity
}
}
- private void setWaitingForResult(boolean value) {
+ private void setWaitingForResult(PendingRequestArgs args) {
boolean isLocked = isWorkspaceLocked();
- mWaitingForResult = value;
+ mPendingRequestArgs = args;
if (isLocked != isWorkspaceLocked()) {
onWorkspaceLockedChanged();
}
@@ -2200,33 +2098,23 @@ public class Launcher extends Activity
}
}
- private void resetAddInfo() {
- mPendingAddInfo.container = ItemInfo.NO_ID;
- mPendingAddInfo.screenId = -1;
- mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
- mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
- mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
- }
-
- void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
- AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) {
+ void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
+ LauncherAppWidgetProviderInfo appWidgetInfo) {
if (LOGD) {
Log.d(TAG, "Adding widget from drop");
}
addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
}
- void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
- final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo,
+ void addAppWidgetImpl(int appWidgetId, ItemInfo info,
+ AppWidgetHostView boundWidget, LauncherAppWidgetProviderInfo appWidgetInfo,
int delay) {
if (appWidgetInfo.configure != null) {
- mPendingAddWidgetInfo = appWidgetInfo;
- mPendingAddWidgetId = appWidgetId;
+ setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, appWidgetInfo, info));
// Launch over to configure widget, if needed
mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
-
} else {
// Otherwise just add it
Runnable onComplete = new Runnable() {
@@ -2237,8 +2125,7 @@ public class Launcher extends Activity
null);
}
};
- completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
- appWidgetInfo);
+ completeAddAppWidget(appWidgetId, info, boundWidget, appWidgetInfo);
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
@@ -2251,17 +2138,22 @@ public class Launcher extends Activity
public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
int[] cell, int spanX, int spanY) {
+ info.container = container;
+ info.screenId = screenId;
+ if (cell != null) {
+ info.cellX = cell[0];
+ info.cellY = cell[1];
+ }
+ info.spanX = spanX;
+ info.spanY = spanY;
+
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- int span[] = new int[2];
- span[0] = spanX;
- span[1] = spanY;
- addAppWidgetFromDrop((PendingAddWidgetInfo) info,
- container, screenId, cell, span);
+ addAppWidgetFromDrop((PendingAddWidgetInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- processShortcutFromDrop(info.componentName, container, screenId, cell);
+ processShortcutFromDrop(info);
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
@@ -2270,51 +2162,17 @@ public class Launcher extends Activity
/**
* Process a shortcut drop.
- *
- * @param componentName The name of the component
- * @param screenId The ID of the screen where it should be added
- * @param cell The cell it should be added to, optional
*/
- private void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
- int[] cell) {
- resetAddInfo();
- mPendingAddInfo.container = container;
- mPendingAddInfo.screenId = screenId;
-
- if (cell != null) {
- mPendingAddInfo.cellX = cell[0];
- mPendingAddInfo.cellY = cell[1];
- }
-
- Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
- createShortcutIntent.setComponent(componentName);
- Utilities.startActivityForResultSafely(this, createShortcutIntent, REQUEST_CREATE_SHORTCUT);
+ private void processShortcutFromDrop(PendingAddItemInfo info) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName);
+ setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info));
+ Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
}
/**
* Process a widget drop.
- *
- * @param info The PendingAppWidgetInfo of the widget being added.
- * @param screenId The ID of the screen where it should be added
- * @param cell The cell it should be added to, optional
*/
- private void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
- int[] cell, int[] span) {
- resetAddInfo();
- mPendingAddInfo.container = info.container = container;
- mPendingAddInfo.screenId = info.screenId = screenId;
- mPendingAddInfo.minSpanX = info.minSpanX;
- mPendingAddInfo.minSpanY = info.minSpanY;
-
- if (cell != null) {
- mPendingAddInfo.cellX = cell[0];
- mPendingAddInfo.cellY = cell[1];
- }
- if (span != null) {
- mPendingAddInfo.spanX = span[0];
- mPendingAddInfo.spanY = span[1];
- }
-
+ private void addAppWidgetFromDrop(PendingAddWidgetInfo info) {
AppWidgetHostView hostView = info.boundWidget;
int appWidgetId;
if (hostView != null) {
@@ -2340,11 +2198,11 @@ public class Launcher extends Activity
if (success) {
addAppWidgetFromDropImpl(appWidgetId, info, null, info.info);
} else {
- mPendingAddWidgetInfo = info.info;
+ setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, info.info, info));
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
- mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+ mAppWidgetManager.getUser(info.info)
.addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
@@ -2563,14 +2421,13 @@ public class Launcher extends Activity
LauncherAppWidgetProviderInfo appWidgetInfo =
mAppWidgetManager.findProvider(info.providerName, info.user);
if (appWidgetInfo != null) {
- mPendingAddWidgetId = info.appWidgetId;
- mPendingAddInfo.copyFrom(info);
- mPendingAddWidgetInfo = appWidgetInfo;
+ setWaitingForResult(PendingRequestArgs
+ .forWidgetInfo(info.appWidgetId, appWidgetInfo, info));
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingAddWidgetId);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, appWidgetInfo.provider);
- mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+ mAppWidgetManager.getUser(appWidgetInfo)
.addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
startActivityForResult(intent, REQUEST_BIND_PENDING_APPWIDGET);
}
@@ -2599,9 +2456,7 @@ public class Launcher extends Activity
private void startRestoredWidgetReconfigActivity(
LauncherAppWidgetProviderInfo provider, LauncherAppWidgetInfo info) {
- mPendingAddWidgetInfo = provider;
- mPendingAddInfo.copyFrom(info);
- mPendingAddWidgetId = info.appWidgetId;
+ setWaitingForResult(PendingRequestArgs.forWidgetInfo(info.appWidgetId, provider, info));
mAppWidgetManager.startConfigActivity(provider,
info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
}
@@ -2771,6 +2626,7 @@ public class Launcher extends Activity
int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
+ setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
.setPackage(pickerPackage)
.putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
@@ -2784,8 +2640,10 @@ public class Launcher extends Activity
*/
public void onClickSettingsButton(View v) {
if (LOGD) Log.d(TAG, "onClickSettingsButton");
- startActivity(new Intent(Utilities.ACTION_APPLICATION_PREFERENCES)
- .setPackage(getPackageName()));
+ Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+ .setPackage(getPackageName());
+ intent.setSourceBounds(getViewBounds(v));
+ startActivity(intent, getActivityLaunchOptions(v));
}
public View.OnTouchListener getHapticFeedbackTouchListener() {
@@ -2809,7 +2667,7 @@ public class Launcher extends Activity
mDragLayer.onAccessibilityStateChanged(enabled);
}
- public void onDragStarted(View view) {
+ public void onDragStarted() {
if (isOnCustomContent()) {
// Custom content screen doesn't participate in drag and drop. If on custom
// content screen, move to default.
@@ -2885,9 +2743,9 @@ public class Launcher extends Activity
&& Intent.ACTION_CALL.equals(intent.getAction())
&& checkSelfPermission(Manifest.permission.CALL_PHONE) !=
PackageManager.PERMISSION_GRANTED) {
- // TODO: Rename sPendingAddItem to a generic name.
- sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
- 0, info);
+
+ setWaitingForResult(PendingRequestArgs
+ .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
REQUEST_PERMISSION_CALL_PHONE);
} else {
@@ -3172,6 +3030,14 @@ public class Launcher extends Activity
}
}
+ public View getTopFloatingView() {
+ View topView = getOpenShortcutsContainer();
+ if (topView == null) {
+ topView = getWorkspace().getOpenFolder();
+ }
+ return topView;
+ }
+
/**
* @return The open shortcuts container, or null if there is none
*/
@@ -3221,7 +3087,7 @@ public class Launcher extends Activity
ItemInfo info = (ItemInfo) v.getTag();
longClickCellInfo = new CellLayout.CellInfo(v, info);
itemUnderLongClick = longClickCellInfo.cell;
- resetAddInfo();
+ mPendingRequestArgs = null;
}
// The hotseat touch handling does not go through Workspace, and we always allow long press
@@ -3243,7 +3109,7 @@ public class Launcher extends Activity
longClickCellInfo.cellX, longClickCellInfo.cellY));
if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
// User long pressed on an item
- mWorkspace.startDrag(longClickCellInfo);
+ mWorkspace.startDrag(longClickCellInfo, new DragOptions());
}
}
}
@@ -3285,29 +3151,6 @@ public class Launcher extends Activity
return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
}
- private void setWorkspaceBackground(int background) {
- switch (background) {
- case WORKSPACE_BACKGROUND_TRANSPARENT:
- getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- break;
- case WORKSPACE_BACKGROUND_BLACK:
- getWindow().setBackgroundDrawable(null);
- break;
- default:
- getWindow().setBackgroundDrawable(mWorkspaceBackgroundDrawable);
- }
- }
-
- protected void changeWallpaperVisiblity(boolean visible) {
- int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
- int curflags = getWindow().getAttributes().flags
- & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
- if (wpflags != curflags) {
- getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
- }
- setWorkspaceBackground(visible ? WORKSPACE_BACKGROUND_GRADIENT : WORKSPACE_BACKGROUND_BLACK);
- }
-
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
@@ -3564,10 +3407,6 @@ public class Launcher extends Activity
// TODO
}
- public boolean launcherCallbacksProvidesSearch() {
- return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
- }
-
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
final boolean result = super.dispatchPopulateAccessibilityEvent(event);
@@ -3955,14 +3794,30 @@ public class Launcher extends Activity
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+
+ boolean isDirectConfig =
+ item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+ if (isDirectConfig && item.bindOptions != null) {
+ Bundle newOptions = item.bindOptions.getExtras();
+ if (options != null) {
+ newOptions.putAll(options);
+ }
+ options = newOptions;
+ }
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
item.appWidgetId, appWidgetInfo, options);
+ // We tried to bind once. If we were not able to bind, we would need to
+ // go through the permission dialog, which means we cannot skip the config
+ // activity.
+ item.bindOptions = null;
+ item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
+
// Bind succeeded
if (success) {
// If the widget has a configure activity, it is still needs to set it up,
// otherwise the widget is ready to go.
- item.restoreStatus = (appWidgetInfo.configure == null)
+ item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
? LauncherAppWidgetInfo.RESTORE_COMPLETED
: LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
}
@@ -4105,21 +3960,10 @@ public class Launcher extends Activity
setWorkspaceLoading(false);
- // If we received the result of any pending adds while the loader was running (e.g. the
- // widget configuration forced an orientation change), process them now.
- if (sPendingAddItem != null) {
- final long screenId = completeAdd(sPendingAddItem);
-
- // TODO: this moves the user to the page where the pending item was added. Ideally,
- // the screen would be guaranteed to exist after bind, and the page would be set through
- // the workspace restore process.
- mWorkspace.post(new Runnable() {
- @Override
- public void run() {
- mWorkspace.snapToScreenId(screenId);
- }
- });
- sPendingAddItem = null;
+ if (mPendingActivityResult != null) {
+ handleActivityResult(mPendingActivityResult.requestCode,
+ mPendingActivityResult.resultCode, mPendingActivityResult.data);
+ mPendingActivityResult = null;
}
InstallShortcutReceiver.disableAndFlushInstallQueue(this);
@@ -4202,6 +4046,10 @@ public class Launcher extends Activity
return Collections.EMPTY_LIST;
}
ComponentName component = info.getTargetComponent();
+ if (component == null) {
+ return Collections.EMPTY_LIST;
+ }
+
List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
return ids == null ? Collections.EMPTY_LIST : ids;
}
@@ -4442,51 +4290,6 @@ public class Launcher extends Activity
}
}
- /**
- * To be overridden by subclasses to indicate that there is an activity to launch
- * before showing the standard launcher experience.
- */
- protected boolean hasFirstRunActivity() {
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.hasFirstRunActivity();
- }
- return false;
- }
-
- /**
- * To be overridden by subclasses to launch any first run activity
- */
- protected Intent getFirstRunActivity() {
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.getFirstRunActivity();
- }
- return null;
- }
-
- private boolean shouldRunFirstRunActivity() {
- return !ActivityManager.isRunningInTestHarness() &&
- !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
- }
-
- public boolean showFirstRunActivity() {
- if (shouldRunFirstRunActivity() &&
- hasFirstRunActivity()) {
- Intent firstRunIntent = getFirstRunActivity();
- if (firstRunIntent != null) {
- startActivity(firstRunIntent);
- markFirstRunActivityShown();
- return true;
- }
- }
- return false;
- }
-
- private void markFirstRunActivityShown() {
- SharedPreferences.Editor editor = mSharedPrefs.edit();
- editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
- editor.apply();
- }
-
private void markAppsViewShown() {
if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
return;
@@ -4510,44 +4313,6 @@ public class Launcher extends Activity
return true;
}
- /**
- * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
- * screen that must be displayed and dismissed.
- */
- protected boolean hasDismissableIntroScreen() {
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.hasDismissableIntroScreen();
- }
- return false;
- }
-
- /**
- * Full screen intro screen to be shown and dismissed before the launcher can be used.
- */
- protected View getIntroScreen() {
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.getIntroScreen();
- }
- return null;
- }
-
- /**
- * To be overriden by subclasses to indicate whether the in-activity intro screen has been
- * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
- */
- private boolean shouldShowIntroScreen() {
- return hasDismissableIntroScreen() &&
- !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
- }
-
- protected void showIntroScreen() {
- View introScreen = getIntroScreen();
- changeWallpaperVisiblity(false);
- if (introScreen != null) {
- mDragLayer.showOverlayView(introScreen);
- }
- }
-
// TODO: These method should be a part of LauncherSearchCallback
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
@@ -4614,8 +4379,8 @@ public class Launcher extends Activity
Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this);
Log.d(TAG, "mSavedState=" + mSavedState);
Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
- Log.d(TAG, "mRestoring=" + mRestoring);
- Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
+ Log.d(TAG, "mPendingRequestArgs=" + mPendingRequestArgs);
+ Log.d(TAG, "mPendingActivityResult=" + mPendingActivityResult);
mModel.dumpState();
// TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 99210fd34..66d895726 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -20,6 +20,7 @@ import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import com.android.launcher3.compat.UserHandleCompat;
@@ -57,6 +58,12 @@ public class LauncherAppWidgetInfo extends ItemInfo {
public static final int FLAG_ID_ALLOCATED = 16;
/**
+ * Indicates that the widget does not need to show config activity, even if it has a
+ * configuration screen. It can also optionally have some extras which are sent during bind.
+ */
+ public static final int FLAG_DIRECT_CONFIG = 32;
+
+ /**
* Indicates that the widget hasn't been instantiated yet.
*/
static final int NO_ID = -1;
@@ -84,6 +91,11 @@ public class LauncherAppWidgetInfo extends ItemInfo {
*/
int installProgress = -1;
+ /**
+ * Optional extras sent during widget bind. See {@link #FLAG_DIRECT_CONFIG}.
+ */
+ public Intent bindOptions;
+
private boolean mHasNotifiedInitialWidgetSizeChanged;
LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
@@ -115,6 +127,8 @@ public class LauncherAppWidgetInfo extends ItemInfo {
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
values.put(LauncherSettings.Favorites.RESTORED, restoreStatus);
+ values.put(LauncherSettings.Favorites.INTENT,
+ bindOptions == null ? null : bindOptions.toUri(0));
}
/**
@@ -129,8 +143,8 @@ public class LauncherAppWidgetInfo extends ItemInfo {
}
@Override
- public String toString() {
- return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
+ protected String dumpProperties() {
+ return super.dumpProperties() + " appWidgetId=" + appWidgetId;
}
public final boolean isWidgetIdAllocated() {
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 2bbe0fbf3..6394b9052 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -79,9 +79,11 @@ public interface LauncherCallbacks {
@Deprecated
public void onWorkspaceLockedChanged();
- public boolean providesSearch();
- public boolean startSearch(String initialQuery, boolean selectInitialQuery,
- Bundle appSearchData, Rect sourceBounds);
+ /**
+ * Starts a search with {@param initialQuery}. Return false if search was not started.
+ */
+ public boolean startSearch(
+ String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
public boolean hasCustomContentToLeft();
public void populateCustomContentContainer();
public View getQsbBar();
@@ -91,10 +93,6 @@ public interface LauncherCallbacks {
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
public UserEventDispatcher getUserEventDispatcher();
- public Intent getFirstRunActivity();
- public boolean hasFirstRunActivity();
- public boolean hasDismissableIntroScreen();
- public View getIntroScreen();
public boolean shouldMoveToDefaultScreenOnHomeIntent();
public boolean hasSettings();
public AllAppsSearchBarController getAllAppsSearchBarController();
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src/com/android/launcher3/LauncherExterns.java
index c7a8668de..887859cb0 100644
--- a/src/com/android/launcher3/LauncherExterns.java
+++ b/src/com/android/launcher3/LauncherExterns.java
@@ -29,4 +29,6 @@ public interface LauncherExterns {
public SharedPreferences getSharedPrefs();
public void setLauncherOverlay(Launcher.LauncherOverlay overlay);
+
+ void clearTypedText();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index c5c52b4bc..68450e7a7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -103,7 +103,6 @@ public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
static final boolean DEBUG_LOADERS = false;
private static final boolean DEBUG_RECEIVER = false;
- private static final boolean REMOVE_UNRESTORED_ICONS = true;
static final String TAG = "Launcher.Model";
@@ -1883,12 +1882,12 @@ public class LauncherModel extends BroadcastReceiver
restored = false;
itemReplaced = true;
- } else if (REMOVE_UNRESTORED_ICONS) {
+ } else {
FileLog.d(TAG, "Unrestored package removed: " + cn);
itemsToRemove.add(id);
continue;
}
- } else if (REMOVE_UNRESTORED_ICONS) {
+ } else {
FileLog.d(TAG, "Unrestored package removed: " + cn);
itemsToRemove.add(id);
continue;
@@ -2148,7 +2147,7 @@ public class LauncherModel extends BroadcastReceiver
// Id would be valid only if the widget restore broadcast was received.
if (isIdValid) {
- status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
} else {
status &= ~LauncherAppWidgetInfo
.FLAG_PROVIDER_NOT_READY;
@@ -2170,7 +2169,7 @@ public class LauncherModel extends BroadcastReceiver
// App restore has started. Update the flag
appWidgetInfo.restoreStatus |=
LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
+ } else if (!isSafeMode) {
FileLog.d(TAG, "Unrestored widget removed: " + component);
itemsToRemove.add(id);
continue;
@@ -2179,6 +2178,14 @@ public class LauncherModel extends BroadcastReceiver
appWidgetInfo.installProgress =
installProgress == null ? 0 : installProgress;
}
+ if (appWidgetInfo.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+ intentDescription = c.getString(intentIndex);
+ if (!TextUtils.isEmpty(intentDescription)) {
+ appWidgetInfo.bindOptions =
+ Intent.parseUri(intentDescription, 0);
+ }
+ }
appWidgetInfo.id = id;
appWidgetInfo.screenId = c.getInt(screenIndex);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index eee562781..f3d949326 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -67,6 +67,7 @@ import com.android.launcher3.util.Thunk;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Locale;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -313,10 +314,32 @@ public class LauncherProvider extends ContentProvider {
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int count = db.delete(args.table, args.where, args.args);
- if (count > 0) notifyListeners();
- reloadLauncherIfExternal();
+ if (Binder.getCallingPid() != Process.myPid()
+ && Favorites.TABLE_NAME.equalsIgnoreCase(args.table)) {
+ String widgetSelection = TextUtils.isEmpty(args.where) ? "1=1" : args.where;
+ widgetSelection = String.format(Locale.ENGLISH, "%1$s = %2$d AND ( %3$s )",
+ Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET, widgetSelection);
+ try (Cursor c = db.query(Favorites.TABLE_NAME, new String[] { Favorites.APPWIDGET_ID },
+ widgetSelection, args.args, null, null, null)) {
+ AppWidgetHost host = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
+ while (c.moveToNext()) {
+ int widgetId = c.getInt(0);
+ if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ try {
+ host.deleteAppWidgetId(widgetId);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error deleting widget id " + widgetId, e);
+ }
+ }
+ }
+ }
+ }
+ int count = db.delete(args.table, args.where, args.args);
+ if (count > 0) {
+ notifyListeners();
+ reloadLauncherIfExternal();
+ }
return count;
}
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 1aaf85bbd..31820d742 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -29,4 +29,9 @@ public class PendingAddItemInfo extends ItemInfo {
* The component that will be created.
*/
public ComponentName componentName;
+
+ @Override
+ protected String dumpProperties() {
+ return super.dumpProperties() + " componentName=" + componentName;
+ }
}
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
index 6ee96fc79..48a75d111 100644
--- a/src/com/android/launcher3/PinchToOverviewListener.java
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -102,8 +102,8 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG
// once the state switching animation is complete.
return false;
}
- if (mWorkspace.getOpenFolder() != null) {
- // Don't listen for the pinch gesture if a folder is open.
+ if (mLauncher.getTopFloatingView() != null) {
+ // Don't listen for the pinch gesture if a floating view is open.
return false;
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 21fa8a05e..fb9374314 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -158,7 +158,7 @@ public class ShortcutInfo extends ItemInfo {
*/
Intent promisedIntent;
- ShortcutInfo() {
+ public ShortcutInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
@@ -270,14 +270,6 @@ public class ShortcutInfo extends ItemInfo {
}
}
- @Override
- public String toString() {
- 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
- + " user=" + user + ")";
- }
-
public ComponentName getTargetComponent() {
return promisedIntent != null ? promisedIntent.getComponent() : intent.getComponent();
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 045b1d33d..b0e096a2e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -58,6 +58,8 @@ import android.util.SparseArray;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.launcher3.compat.UserHandleCompat;
@@ -168,10 +170,6 @@ public final class Utilities {
return false;
}
- // TODO: Use Intent.ACTION_APPLICATION_PREFERENCES when N SDK is available.
- public static final String ACTION_APPLICATION_PREFERENCES
- = "android.intent.action.APPLICATION_PREFERENCES";
-
public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
byte[] data = c.getBlob(iconIndex);
try {
@@ -688,11 +686,11 @@ public final class Utilities {
/**
* Calculates the height of a given string at a specific text size.
*/
- public static float calculateTextHeight(float textSizePx) {
+ public static int calculateTextHeight(float textSizePx) {
Paint p = new Paint();
p.setTextSize(textSizePx);
Paint.FontMetrics fm = p.getFontMetrics();
- return -fm.top + fm.bottom;
+ return (int) Math.ceil(fm.bottom - fm.top);
}
/**
@@ -765,16 +763,21 @@ public final class Utilities {
}
public static boolean isBootCompleted() {
+ return "1".equals(getSystemProperty("sys.boot_completed", "1"));
+ }
+
+ public static String getSystemProperty(String property, String defaultValue) {
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);
+ String value = (String) getter.invoke(null, property);
+ if (!TextUtils.isEmpty(value)) {
+ return value;
+ }
} catch (Exception e) {
Log.d(TAG, "Unable to read system properties");
- // Assume that boot has completed
- return true;
}
+ return defaultValue;
}
/**
@@ -905,4 +908,15 @@ public final class Utilities {
ta.recycle();
return colorAccent;
}
+
+ public static void sendCustomAccessibilityEvent(View target, int type, String text) {
+ AccessibilityManager accessibilityManager = (AccessibilityManager)
+ target.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityManager.isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ target.onInitializeAccessibilityEvent(event);
+ event.getText().add(text);
+ accessibilityManager.sendAccessibilityEvent(event);
+ }
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 75692a686..c499beeb3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -59,7 +59,7 @@ import android.widget.TextView;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.accessibility.AccessibileDragListenerAdapter;
import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
@@ -69,13 +69,13 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragScroller;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutsContainerListener;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -84,6 +84,7 @@ import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.MultiStateAlphaController;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -100,7 +101,7 @@ import java.util.HashSet;
public class Workspace extends PagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
- Insettable, DropTargetSource, AccessibilityDragSource {
+ Insettable, DropTargetSource {
private static final String TAG = "Launcher.Workspace";
private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
@@ -134,7 +135,6 @@ public class Workspace extends PagedView
@Thunk Runnable mRemoveEmptyScreenRunnable;
@Thunk boolean mDeferRemoveExtraEmptyScreen = false;
- @Thunk boolean mAddNewPageOnDrag = true;
/**
* CellInfo for the cell that is currently being dragged
@@ -247,8 +247,7 @@ public class Workspace extends PagedView
/** Is the user is dragging an item near the edge of a page? */
private boolean mInScrollArea = false;
- private HolographicOutlineHelper mOutlineHelper;
- @Thunk Bitmap mDragOutline = null;
+ private DragPreviewProvider mOutlineProvider = null;
public static final int DRAG_BITMAP_PADDING = DragPreviewProvider.DRAG_BITMAP_PADDING;
private boolean mWorkspaceFadeInAdjacentScreens;
@@ -343,8 +342,6 @@ public class Workspace extends PagedView
public Workspace(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mOutlineHelper = HolographicOutlineHelper.obtain(context);
-
mLauncher = (Launcher) context;
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
final Resources res = getResources();
@@ -412,25 +409,56 @@ public class Workspace extends PagedView
}
@Override
- public void onDragStart(final DragSource source, ItemInfo info, int dragAction) {
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
if (ENFORCE_DRAG_EVENT_ORDER) {
enfoceDragParity("onDragStart", 0, 0);
}
+ if (mOutlineProvider != null) {
+ // The outline is used to visualize where the item will land if dropped
+ mOutlineProvider.generateDragOutline(mCanvas);
+ }
+
updateChildrenLayersEnabled(false);
+ mLauncher.onDragStarted();
mLauncher.lockScreenOrientation();
mLauncher.onInteractionBegin();
// Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
InstallShortcutReceiver.enableInstallQueue();
- if (mAddNewPageOnDrag) {
+ // Do not add a new page if it is a accessible drag which was not started by the workspace.
+ // We do not support accessibility drag from other sources and instead provide a direct
+ // action for move/add to homescreen.
+ // When a accessible drag is started by the folder, we only allow rearranging withing the
+ // folder.
+ boolean addNewPage = !(options.isAccessibleDrag && dragObject.dragSource != this);
+
+ if (addNewPage) {
mDeferRemoveExtraEmptyScreen = false;
addExtraEmptyScreenOnDrag();
+
+ if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+ && dragObject.dragSource != this) {
+ // When dragging a widget from different source, move to a page which has
+ // enough space to place this widget (after rearranging/resizing). We special case
+ // widgets as they cannot be placed inside a folder.
+ // 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 currentPage = getPageNearestToCenterOfScreen();
+ for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
+ CellLayout page = (CellLayout) getPageAt(pageIndex);
+ if (page.hasReorderSolution(dragObject.dragInfo)) {
+ setCurrentPage(pageIndex);
+ break;
+ }
+ }
+ }
}
- }
- public void setAddNewPageOnDrag(boolean addPage) {
- mAddNewPageOnDrag = addPage;
+ if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+ // Always enter the spring loaded mode
+ mLauncher.enterSpringLoadedDragMode();
+ }
}
public void deferRemoveExtraEmptyScreen() {
@@ -566,7 +594,31 @@ public class Workspace extends PagedView
}
// Add the first page
CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
-
+ if (FeatureFlags.PULLDOWN_SEARCH) {
+ firstPage.setOnTouchListener(new VerticalFlingDetector(mLauncher) {
+ // detect fling when touch started from empty space
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (workspaceInModalState()) return false;
+ if (shouldConsumeTouch(v)) return true;
+ if (super.onTouch(v, ev)) {
+ mLauncher.startSearch("", false, null, false);
+ }
+ return false;
+ }
+ });
+ firstPage.setOnInterceptTouchListener(new VerticalFlingDetector(mLauncher) {
+ // detect fling when touch started from on top of the icons
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (shouldConsumeTouch(v)) return true;
+ if (super.onTouch(v, ev)) {
+ mLauncher.startSearch("", false, null, false);
+ }
+ return false;
+ }
+ });
+ }
// Always add a QSB on the first screen.
if (qsb == null) {
// In transposed layout, we add the QSB in the Grid. As workspace does not touch the
@@ -600,8 +652,8 @@ public class Workspace extends PagedView
ViewGroup.LayoutParams lp = qsbContainer.getLayoutParams();
if (cellHeight > 0 && lp.height != cellHeight) {
lp.height = cellHeight;
+ qsbContainer.setLayoutParams(lp);
}
- qsbContainer.setLayoutParams(lp);
}
}
@@ -658,7 +710,6 @@ public class Workspace extends PagedView
// created CellLayout.
CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
-
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
newScreen.setSoundEffectsEnabled(false);
@@ -1144,6 +1195,10 @@ public class Workspace extends PagedView
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
+ return shouldConsumeTouch(v);
+ }
+
+ private boolean shouldConsumeTouch(View v) {
return (workspaceInModalState() || !isFinishedSwitchingState())
|| (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
}
@@ -1698,26 +1753,6 @@ public class Workspace extends PagedView
}
}
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- @Override
- public void enableAccessibleDrag(boolean enable) {
- for (int i = 0; i < getChildCount(); i++) {
- CellLayout child = (CellLayout) getChildAt(i);
- child.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
- }
-
- if (enable) {
- // We need to allow our individual children to become click handlers in this case
- setOnClickListener(null);
- } else {
- // Reset our click listener
- setOnClickListener(mLauncher);
- }
- mLauncher.getDropTargetBar().enableAccessibleDrag(enable);
- mLauncher.getHotseat().getLayout()
- .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
- }
-
public boolean hasCustomContent() {
return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
}
@@ -1850,19 +1885,6 @@ public class Workspace extends PagedView
}
@Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- if (!mLauncher.isAppsViewVisible()) {
- final Folder openFolder = getOpenFolder();
- if (openFolder != null) {
- return openFolder.requestFocus(direction, previouslyFocusedRect);
- } else {
- return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
- }
- }
- return false;
- }
-
- @Override
public int getDescendantFocusability() {
if (workspaceInModalState()) {
return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
@@ -1870,18 +1892,6 @@ public class Workspace extends PagedView
return super.getDescendantFocusability();
}
- @Override
- public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- if (!mLauncher.isAppsViewVisible()) {
- final Folder openFolder = getOpenFolder();
- if (openFolder != null) {
- openFolder.addFocusables(views, direction);
- } else {
- super.addFocusables(views, direction, focusableMode);
- }
- }
- }
-
public boolean workspaceInModalState() {
return mState != State.NORMAL;
}
@@ -1999,23 +2009,8 @@ public class Workspace extends PagedView
position[0], position[1], 0, null);
}
- 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
- mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
+ public void prepareDragWithProvider(DragPreviewProvider outlineProvider) {
+ mOutlineProvider = outlineProvider;
}
public void exitWidgetResizeMode() {
@@ -2271,40 +2266,7 @@ public class Workspace extends PagedView
return null;
}
- /**
- * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
- * Responsibility for the bitmap is transferred to the caller.
- */
- private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
- boolean clipAlpha) {
- final int outlineColor = getResources().getColor(R.color.outline_color);
- final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- mCanvas.setBitmap(b);
-
- Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
- float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
- (h - padding) / (float) orig.getHeight());
- int scaledWidth = (int) (scaleFactor * orig.getWidth());
- int scaledHeight = (int) (scaleFactor * orig.getHeight());
- Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
-
- // center the image
- dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
-
- mCanvas.drawBitmap(orig, src, dst, null);
- mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
- clipAlpha);
- mCanvas.setBitmap(null);
-
- return b;
- }
-
- public void startDrag(CellLayout.CellInfo cellInfo) {
- startDrag(cellInfo, false);
- }
-
- @Override
- public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) {
+ public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
View child = cellInfo.cell;
// Make sure the drag was started by a long press as opposed to a long click.
@@ -2317,10 +2279,25 @@ public class Workspace extends PagedView
CellLayout layout = (CellLayout) child.getParent().getParent();
layout.prepareChildForDrag(child);
- beginDragShared(child, this, accessible);
+ if (options.isAccessibleDrag) {
+ mDragController.addDragListener(new AccessibileDragListenerAdapter(
+ this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) {
+ @Override
+ protected void enableAccessibleDrag(boolean enable) {
+ super.enableAccessibleDrag(enable);
+ setEnableForLayout(mLauncher.getHotseat().getLayout(),enable);
+
+ // We need to allow our individual children to become click handlers in this
+ // case, so temporarily unset the click handlers.
+ setOnClickListener(enable ? null : mLauncher);
+ }
+ });
+ }
+
+ beginDragShared(child, this, options);
}
- public void beginDragShared(View child, DragSource source, boolean accessible) {
+ public void beginDragShared(View child, DragSource source, DragOptions options) {
Object dragObject = child.getTag();
if (!(dragObject instanceof ItemInfo)) {
String msg = "Drag started with a view that has no tag set. This "
@@ -2328,20 +2305,17 @@ public class Workspace extends PagedView
+ "View: " + child + " tag: " + child.getTag();
throw new IllegalStateException(msg);
}
- beginDragShared(child, source, accessible, (ItemInfo) dragObject,
- new DragPreviewProvider(child));
+ beginDragShared(child, source, (ItemInfo) dragObject,
+ new DragPreviewProvider(child), options);
}
- public DragView beginDragShared(View child, DragSource source, boolean accessible,
- ItemInfo dragObject, DragPreviewProvider previewProvider) {
+ public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
+ DragPreviewProvider previewProvider, DragOptions dragOptions) {
child.clearFocus();
child.setPressed(false);
+ mOutlineProvider = previewProvider;
- // The outline is used to visualize where the item will land if dropped
- mDragOutline = previewProvider.createDragOutline(mCanvas);
-
- mLauncher.onDragStarted(child);
// The drag bitmap follows the touch point around on the screen
final Bitmap b = previewProvider.createDragBitmap(mCanvas);
int halfPadding = previewProvider.previewPadding / 2;
@@ -2381,15 +2355,9 @@ public class Workspace extends PagedView
}
DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
- dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
- dragRect, scale, accessible);
+ dragObject, dragVisualizeOffset, dragRect, scale, dragOptions);
dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
-
b.recycle();
-
- if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
- mLauncher.enterSpringLoadedDragMode();
- }
return dv;
}
@@ -2966,7 +2934,7 @@ public class Workspace extends PagedView
private void cleanupAddToFolder() {
if (mDragOverFolderIcon != null) {
- mDragOverFolderIcon.onDragExit(null);
+ mDragOverFolderIcon.onDragExit();
mDragOverFolderIcon = null;
}
}
@@ -3180,7 +3148,7 @@ public class Workspace extends PagedView
item.spanY, child, mTargetCell);
if (!nearestDropOccupied) {
- mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+ mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
&& !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
@@ -3325,7 +3293,7 @@ public class Workspace extends PagedView
}
boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
- mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+ mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
}
}
@@ -3717,7 +3685,7 @@ public class Workspace extends PagedView
&& mDragInfo.cell != null) {
mDragInfo.cell.setVisibility(VISIBLE);
}
- mDragOutline = null;
+ mOutlineProvider = null;
mDragInfo = null;
if (!isFlingToDelete) {
diff --git a/src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java b/src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java
new file mode 100644
index 000000000..62a9a6d19
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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.accessibility;
+
+import android.view.ViewGroup;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.dragndrop.DragController.DragListener;
+import com.android.launcher3.dragndrop.DragOptions;
+
+/**
+ * Utility listener to enable/disable accessibility drag flags for a ViewGroup
+ * containing CellLayouts
+ */
+public class AccessibileDragListenerAdapter implements DragListener {
+
+ private final ViewGroup mViewGroup;
+ private final int mDragType;
+
+ /**
+ * @param parent
+ * @param dragType either {@link CellLayout#WORKSPACE_ACCESSIBILITY_DRAG} or
+ * {@link CellLayout#FOLDER_ACCESSIBILITY_DRAG}
+ */
+ public AccessibileDragListenerAdapter(ViewGroup parent, int dragType) {
+ mViewGroup = parent;
+ mDragType = dragType;
+ }
+
+ @Override
+ public void onDragStart(DragObject dragObject, DragOptions options) {
+ enableAccessibleDrag(true);
+ }
+
+ @Override
+ public void onDragEnd() {
+ enableAccessibleDrag(false);
+ Launcher.getLauncher(mViewGroup.getContext()).getDragController().removeDragListener(this);
+ }
+
+ protected void enableAccessibleDrag(boolean enable) {
+ for (int i = 0; i < mViewGroup.getChildCount(); i++) {
+ setEnableForLayout((CellLayout) mViewGroup.getChildAt(i), enable);
+ }
+ }
+
+ protected final void setEnableForLayout(CellLayout layout, boolean enable) {
+ layout.enableAccessibleDrag(enable, mDragType);
+ }
+}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0562cf54b..173aad044 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -22,6 +22,8 @@ import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InfoDropTarget;
@@ -73,7 +75,6 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
@Thunk final Launcher mLauncher;
private DragInfo mDragInfo = null;
- private AccessibilityDragSource mDragSource = null;
public LauncherAccessibilityDelegate(Launcher launcher) {
mLauncher = launcher;
@@ -372,26 +373,25 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
Folder folder = workspace.getOpenFolder();
if (folder != null) {
- if (folder.getItemsInReadingOrder().contains(item)) {
- mDragSource = folder;
- } else {
+ if (!folder.getItemsInReadingOrder().contains(item)) {
mLauncher.closeFolder();
+ folder = null;
}
}
- if (mDragSource == null) {
- mDragSource = workspace;
- }
- mDragSource.enableAccessibleDrag(true);
- mDragSource.startDrag(cellInfo, true);
- if (mLauncher.getDragController().isDragging()) {
- mLauncher.getDragController().addDragListener(this);
+ mLauncher.getDragController().addDragListener(this);
+
+ DragOptions options = new DragOptions();
+ options.isAccessibleDrag = true;
+ if (folder != null) {
+ folder.startDrag(cellInfo.cell, options);
+ } else {
+ workspace.startDrag(cellInfo, options);
}
}
-
@Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+ public void onDragStart(DragObject dragObject, DragOptions options) {
// No-op
}
@@ -399,16 +399,6 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
public void onDragEnd() {
mLauncher.getDragController().removeDragListener(this);
mDragInfo = null;
- if (mDragSource != null) {
- mDragSource.enableAccessibleDrag(false);
- mDragSource = null;
- }
- }
-
- public static interface AccessibilityDragSource {
- void startDrag(CellLayout.CellInfo cellInfo, boolean accessible);
-
- void enableAccessibleDrag(boolean enable);
}
/**
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index ff70279ea..0baa8f3db 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -25,6 +25,7 @@ import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.shortcuts.DeepShortcutView;
import java.util.ArrayList;
@@ -46,7 +47,10 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele
@Override
public boolean performAction(View host, ItemInfo item, int action) {
if (action == ADD_TO_WORKSPACE) {
- final ShortcutInfo info = (ShortcutInfo) item;
+ if (!(host.getParent() instanceof DeepShortcutView)) {
+ return false;
+ }
+ final ShortcutInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
final int[] coordinates = new int[2];
final long screenId = findSpaceOnWorkspace(item, coordinates);
Runnable onComplete = new Runnable() {
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index dafa73fec..cfd07e658 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -96,14 +96,14 @@ public class AllAppsBackgroundDrawable extends Drawable {
public AllAppsBackgroundDrawable(Context context) {
Resources res = context.getResources();
mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
- 0.575f, 0.1f, Gravity.CENTER_HORIZONTAL);
+ 0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
mIcons = new TransformedImageDrawable[4];
mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
0.375f, 0, Gravity.CENTER_HORIZONTAL);
mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
- 0.3125f, 0.25f, Gravity.CENTER_HORIZONTAL);
+ 0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
- 0.475f, 0.4f, Gravity.CENTER_HORIZONTAL);
+ 0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index a8e2140c2..290accb1e 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -25,6 +25,7 @@ import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -48,10 +49,10 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.keyboard.FocusedItemDecorator;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
@@ -277,6 +278,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
return false;
}
+
+ // IF a shortcuts container is open, container should not be pulled down.
+ if (mLauncher.getOpenShortcutsContainer() != null) {
+ return false;
+ }
+
// IF scroller is at the very top OR there is no scroll bar because there is probably not
// enough items to scroll, THEN it's okay for the container to be pulled down.
if (mAppsRecyclerView.getScrollBar().getThumbOffset().y <= 0) {
@@ -539,7 +546,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
if (!mLauncher.isDraggingEnabled()) return false;
// Start the drag
- mLauncher.getWorkspace().beginDragShared(v, this, false);
+ mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
// Enter spring loaded mode
mLauncher.enterSpringLoadedDragMode();
@@ -707,4 +714,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = mAppsRecyclerView.getContainerType(v);
}
+
+ public boolean shouldRestoreImeState() {
+ return !TextUtils.isEmpty(mSearchInput.getText());
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 9a48367cd..365ab3185 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -127,10 +127,9 @@ public abstract class AllAppsSearchBarController
@Override
public boolean onBackKey() {
- // Only hide the search field if there is no query, or if there
- // are no filtered results
+ // Only hide the search field if there is no query
String query = Utilities.trim(mInput.getEditableText().toString());
- if (query.isEmpty() || mApps.hasNoFilteredResults()) {
+ if (query.isEmpty()) {
reset();
return true;
}
@@ -163,8 +162,7 @@ public abstract class AllAppsSearchBarController
* Focuses the search field to handle key events.
*/
public void focusSearchField() {
- mInput.requestFocus();
- mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
+ mInput.showKeyboard();
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 41d639379..1719b0594 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -6,6 +6,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.Log;
import android.view.MotionEvent;
@@ -101,7 +102,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
R.dimen.all_apps_bezel_swipe_height);
mEvaluator = new ArgbEvaluator();
- mAllAppsBackgroundColor = l.getColor(R.color.all_apps_container_color);
+ mAllAppsBackgroundColor = ContextCompat.getColor(l, R.color.all_apps_container_color);
}
@Override
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
index 10740ec77..ac22dd279 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
@@ -22,15 +22,12 @@ import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
import java.util.List;
-import java.util.regex.Pattern;
/**
* The default search implementation.
*/
public class DefaultAppSearchAlgorithm {
- private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+");
-
private final List<AppInfo> mApps;
protected final Handler mResultHandler;
@@ -61,34 +58,79 @@ public class DefaultAppSearchAlgorithm {
// Do an intersection of the words in the query and each title, and filter out all the
// apps that don't match all of the words in the query.
final String queryTextLower = query.toLowerCase();
- final String[] queryWords = SPLIT_PATTERN.split(queryTextLower);
-
final ArrayList<ComponentKey> result = new ArrayList<>();
for (AppInfo info : mApps) {
- if (matches(info, queryWords)) {
+ if (matches(info, queryTextLower)) {
result.add(info.toComponentKey());
}
}
return result;
}
- protected boolean matches(AppInfo info, String[] queryWords) {
+ protected boolean matches(AppInfo info, String query) {
+ int queryLength = query.length();
+
String title = info.title.toString();
- String[] words = SPLIT_PATTERN.split(title.toLowerCase());
- for (int qi = 0; qi < queryWords.length; qi++) {
- boolean foundMatch = false;
- for (int i = 0; i < words.length; i++) {
- if (words[i].startsWith(queryWords[qi])) {
- foundMatch = true;
- break;
- }
+ int titleLength = title.length();
+
+ if (titleLength < queryLength || queryLength <= 0) {
+ return false;
+ }
+
+ int lastType;
+ int thisType = Character.UNASSIGNED;
+ int nextType = Character.getType(title.codePointAt(0));
+
+ int end = titleLength - queryLength;
+ for (int i = 0; i <= end; i++) {
+ lastType = thisType;
+ thisType = nextType;
+ nextType = i < (titleLength - 1) ?
+ Character.getType(title.codePointAt(i + 1)) : Character.UNASSIGNED;
+ if (isBreak(thisType, lastType, nextType) &&
+ title.substring(i, i + queryLength).equalsIgnoreCase(query)) {
+ return true;
}
- if (!foundMatch) {
- // If there is a word in the query that does not match any words in this
- // title, so skip it.
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the current point should be a break point. Following cases
+ * are considered as break points:
+ * 1) Any non space character after a space character
+ * 2) Any digit after a non-digit character
+ * 3) Any capital character after a digit or small character
+ * 4) Any capital character before a small character
+ */
+ protected boolean isBreak(int thisType, int prevType, int nextType) {
+ switch (thisType) {
+ case Character.UPPERCASE_LETTER:
+ if (nextType == Character.UPPERCASE_LETTER) {
+ return true;
+ }
+ // Follow through
+ case Character.TITLECASE_LETTER:
+ // Break point if previous was not a upper case
+ return prevType != Character.UPPERCASE_LETTER;
+ case Character.LOWERCASE_LETTER:
+ // Break point if previous was not a letter.
+ return prevType > Character.OTHER_LETTER;
+ case Character.DECIMAL_DIGIT_NUMBER:
+ case Character.LETTER_NUMBER:
+ case Character.OTHER_NUMBER:
+ // Break point if previous was not a number
+ return !(prevType == Character.DECIMAL_DIGIT_NUMBER
+ || prevType == Character.LETTER_NUMBER
+ || prevType == Character.OTHER_NUMBER);
+ case Character.MATH_SYMBOL:
+ case Character.CURRENCY_SYMBOL:
+ case Character.OTHER_PUNCTUATION:
+ case Character.DASH_PUNCTUATION:
+ // Always a break point for a symbol
+ return true;
+ default:
return false;
- }
}
- return true;
}
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 29ed5d9ba..a5f8dd296 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -32,8 +32,12 @@ public abstract class UserManagerCompat {
public static UserManagerCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
- if (Utilities.isNycOrAbove()) {
+ if (Utilities.isNycMR1OrAbove()) {
+ sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
+ } else if (Utilities.isNycOrAbove()) {
sInstance = new UserManagerCompatVN(context.getApplicationContext());
+ } else if (Utilities.ATLEAST_MARSHMALLOW) {
+ sInstance = new UserManagerCompatVM(context.getApplicationContext());
} else if (Utilities.ATLEAST_LOLLIPOP) {
sInstance = new UserManagerCompatVL(context.getApplicationContext());
} else if (Utilities.ATLEAST_JB_MR1) {
@@ -58,4 +62,6 @@ public abstract class UserManagerCompat {
public abstract long getUserCreationTime(UserHandleCompat user);
public abstract boolean isQuietModeEnabled(UserHandleCompat user);
public abstract boolean isUserUnlocked(UserHandleCompat user);
+
+ public abstract boolean isDemoUser();
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index e678ffa3d..9bd4567a1 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -60,4 +60,9 @@ public class UserManagerCompatV16 extends UserManagerCompat {
public boolean isUserUnlocked(UserHandleCompat user) {
return true;
}
+
+ @Override
+ public boolean isDemoUser() {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index c53d702b7..2552b0c2c 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2014 The Android Open Source Project
*
@@ -94,9 +93,6 @@ public class UserManagerCompatVL extends UserManagerCompatV17 {
@Override
public long getUserCreationTime(UserHandleCompat user) {
- if (Utilities.ATLEAST_MARSHMALLOW) {
- return mUserManager.getUserCreationTime(user.getUser());
- }
SharedPreferences prefs = Utilities.getPrefs(mContext);
String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
if (!prefs.contains(key)) {
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVM.java b/src/com/android/launcher3/compat/UserManagerCompatVM.java
new file mode 100644
index 000000000..81d67ea43
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVM.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.M)
+public class UserManagerCompatVM extends UserManagerCompatVL {
+
+ UserManagerCompatVM(Context context) {
+ super(context);
+ }
+
+ @Override
+ public long getUserCreationTime(UserHandleCompat user) {
+ return mUserManager.getUserCreationTime(user.getUser());
+ }
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
index 771d141c3..4edac0522 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -20,10 +20,10 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
-@TargetApi(Build.VERSION_CODES.N)
-public class UserManagerCompatVN extends UserManagerCompatVL {
+import com.android.launcher3.Utilities;
- private static final String TAG = "UserManagerCompatVN";
+@TargetApi(Build.VERSION_CODES.N)
+public class UserManagerCompatVN extends UserManagerCompatVM {
UserManagerCompatVN(Context context) {
super(context);
@@ -36,12 +36,7 @@ public class UserManagerCompatVN extends UserManagerCompatVL {
@Override
public boolean isUserUnlocked(UserHandleCompat user) {
- // TODO: Remove the try-catch block when the API permission has been relaxed (b/30475753)
- try {
- return mUserManager.isUserUnlocked(user.getUser());
- } catch (RuntimeException e) {
- return !isQuietModeEnabled(user);
- }
+ return mUserManager.isUserUnlocked(user.getUser());
}
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
new file mode 100644
index 000000000..3f64bc863
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.N_MR1)
+public class UserManagerCompatVNMr1 extends UserManagerCompatVN {
+
+ UserManagerCompatVNMr1(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isDemoUser() {
+ return mUserManager.isDemoUser();
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
new file mode 100644
index 000000000..156941a29
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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 android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+/**
+ * DragSource used when the drag started at another window.
+ */
+public class AnotherWindowDragSource implements DragSource {
+
+ private final Context mContext;
+
+ AnotherWindowDragSource(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public boolean supportsFlingToDelete() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsAppInfoDropTarget() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDeleteDropTarget() {
+ return false;
+ }
+
+ @Override
+ public float getIntrinsicIconScaleFactor() {
+ return 1;
+ }
+
+ @Override
+ public void onFlingToDeleteCompleted() {
+ }
+
+ @Override
+ public void onDropCompleted(View target, DragObject d,
+ boolean isFlingToDelete, boolean success) {
+ if (!success) {
+ Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null);
+ }
+
+ }
+
+ @Override
+ public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+ // TODO: Probably log something
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index b57f5bf06..a93ee9019 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -25,7 +25,6 @@ import android.graphics.PointF;
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;
@@ -57,12 +56,6 @@ import java.util.ArrayList;
public class DragController implements DragDriver.EventListener, TouchController {
private static final String TAG = "Launcher.DragController";
- /** Indicates the drag is a move. */
- public static int DRAG_ACTION_MOVE = 0;
-
- /** Indicates the drag is a copy. */
- public static int DRAG_ACTION_COPY = 1;
-
public static final int SCROLL_DELAY = 500;
public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
@@ -91,8 +84,8 @@ public class DragController implements DragDriver.EventListener, TouchController
*/
private DragDriver mDragDriver = null;
- /** Whether or not an accessible drag operation is in progress. */
- private boolean mIsAccessibleDrag;
+ /** Options controlling the drag behavior. */
+ private DragOptions mOptions;
/** X coordinate of the down event. */
private int mMotionDownX;
@@ -145,12 +138,10 @@ public class DragController implements DragDriver.EventListener, TouchController
/**
* A drag has begun
*
- * @param source An object representing where the drag originated
- * @param info The data associated with the object that is being dragged
- * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
- * or {@link DragController#DRAG_ACTION_COPY}
+ * @param dragObject The object being dragged
+ * @param options Options used to start the drag
*/
- void onDragStart(DragSource source, ItemInfo info, int dragAction);
+ void onDragStart(DropTarget.DragObject dragObject, DragOptions options);
/**
* The drag has ended
@@ -160,8 +151,6 @@ public class DragController implements DragDriver.EventListener, TouchController
/**
* Used to create a new DragLayer from XML.
- *
- * @param context The application's context.
*/
public DragController(Launcher launcher) {
Resources r = launcher.getResources();
@@ -183,11 +172,9 @@ public class DragController implements DragDriver.EventListener, TouchController
* @param source An object representing where the drag originated
* @param dragInfo The data associated with the object that is being dragged
* @param viewImageBounds the position of the image inside the view
- * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
- * {@link #DRAG_ACTION_COPY}
*/
public void startDrag(View v, Bitmap bmp, DragSource source, ItemInfo dragInfo,
- Rect viewImageBounds, int dragAction, float initialDragViewScale) {
+ Rect viewImageBounds, float initialDragViewScale, DragOptions options) {
int[] loc = mCoordinatesTemp;
mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
int dragLayerX = loc[0] + viewImageBounds.left
@@ -195,12 +182,8 @@ public class DragController implements DragDriver.EventListener, TouchController
int dragLayerY = loc[1] + viewImageBounds.top
+ (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
- startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null,
- null, initialDragViewScale, false);
-
- if (dragAction == DRAG_ACTION_MOVE) {
- v.setVisibility(View.GONE);
- }
+ startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, null,
+ null, initialDragViewScale, options);
}
/**
@@ -212,15 +195,12 @@ public class DragController implements DragDriver.EventListener, TouchController
* @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
* @param source An object representing where the drag originated
* @param dragInfo The data associated with the object that is being dragged
- * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
- * {@link #DRAG_ACTION_COPY}
* @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
- * @param accessible whether this drag should occur in accessibility mode
*/
public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
- DragSource source, ItemInfo dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
- float initialDragViewScale, boolean accessible) {
+ DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
+ float initialDragViewScale, DragOptions options) {
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
@@ -232,8 +212,10 @@ public class DragController implements DragDriver.EventListener, TouchController
}
mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
- for (DragListener listener : mListeners) {
- listener.onDragStart(source, dragInfo, dragAction);
+ mOptions = options;
+ if (mOptions.systemDndStartPoint != null) {
+ mMotionDownX = mOptions.systemDndStartPoint.x;
+ mMotionDownY = mOptions.systemDndStartPoint.y;
}
final int registrationX = mMotionDownX - dragLayerX;
@@ -242,7 +224,6 @@ public class DragController implements DragDriver.EventListener, TouchController
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
- mIsAccessibleDrag = accessible;
mLastDropTarget = null;
mDragObject = new DropTarget.DragObject();
@@ -254,7 +235,7 @@ public class DragController implements DragDriver.EventListener, TouchController
registrationY, initialDragViewScale, scaleDps);
mDragObject.dragComplete = false;
- if (mIsAccessibleDrag) {
+ if (mOptions.isAccessibleDrag) {
// For an accessible drag, we assume the view is being dragged from the center.
mDragObject.xOffset = b.getWidth() / 2;
mDragObject.yOffset = b.getHeight() / 2;
@@ -264,7 +245,7 @@ public class DragController implements DragDriver.EventListener, TouchController
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
- mDragDriver = DragDriver.create(this, dragInfo, dragView);
+ mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
}
mDragObject.dragSource = source;
@@ -282,6 +263,11 @@ public class DragController implements DragDriver.EventListener, TouchController
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
dragView.show(mMotionDownX, mMotionDownY);
mDistanceSinceScroll = 0;
+
+ for (DragListener listener : new ArrayList<>(mListeners)) {
+ listener.onDragStart(mDragObject, mOptions);
+ }
+
mLastTouch[0] = mMotionDownX;
mLastTouch[1] = mMotionDownY;
handleMoveEvent(mMotionDownX, mMotionDownY);
@@ -308,7 +294,11 @@ public class DragController implements DragDriver.EventListener, TouchController
}
public boolean isDragging() {
- return mDragDriver != null || mIsAccessibleDrag;
+ return mDragDriver != null || (mOptions != null && mOptions.isAccessibleDrag);
+ }
+
+ public boolean isExternalDrag() {
+ return (mOptions != null && mOptions.systemDndStartPoint != null);
}
/**
@@ -343,7 +333,7 @@ public class DragController implements DragDriver.EventListener, TouchController
private void endDrag() {
if (isDragging()) {
mDragDriver = null;
- mIsAccessibleDrag = false;
+ mOptions = null;
clearScrollRunnable();
boolean isDeferred = false;
if (mDragObject.dragView != null) {
@@ -422,10 +412,6 @@ public class DragController implements DragDriver.EventListener, TouchController
@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;
@@ -454,14 +440,7 @@ public class DragController implements DragDriver.EventListener, TouchController
* Call this from a drag source view.
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
- @SuppressWarnings("all") // suppress dead code warning
- final boolean debug = false;
- if (debug) {
- Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " Dragging="
- + (mDragDriver != null));
- }
-
- if (mIsAccessibleDrag) {
+ if (mOptions != null && mOptions.isAccessibleDrag) {
return false;
}
@@ -604,7 +583,7 @@ public class DragController implements DragDriver.EventListener, TouchController
* Call this from a drag source view.
*/
public boolean onTouchEvent(MotionEvent ev) {
- if (mDragDriver == null || mIsAccessibleDrag) {
+ if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
return false;
}
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 2164708ba..4db8c07d5 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -17,18 +17,19 @@
package com.android.launcher3.dragndrop;
import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
import android.content.Intent;
-import android.graphics.Canvas;
-import android.graphics.Point;
import android.view.DragEvent;
import android.view.MotionEvent;
-import android.view.View;
-import com.android.launcher3.AnotherWindowDropTarget;
import com.android.launcher3.DropTarget;
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
/**
* Base class for driving a drag/drop operation.
@@ -50,7 +51,7 @@ public abstract class DragDriver {
/**
* Handles ending of the DragView animation.
*/
- public abstract void onDragViewAnimationEnd();
+ public void onDragViewAnimationEnd() { }
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
@@ -89,100 +90,46 @@ public abstract class DragDriver {
return true;
}
- public static DragDriver create(
- DragController dragController, ItemInfo dragInfo, DragView dragView) {
- if (FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER && Utilities.isNycOrAbove()) {
- return new SystemDragDriver(dragController, dragInfo.getIntent(), dragView);
+ public static DragDriver create(Context context, DragController dragController,
+ DragObject dragObject, DragOptions options) {
+ if (Utilities.isNycOrAbove() && options.systemDndStartPoint != null) {
+ return new SystemDragDriver(dragController, context, dragObject);
} 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;
+ private final DragObject mDragObject;
+ private final Context mContext;
+
boolean mReceivedDropEvent = false;
float mLastX = 0;
float mLastY = 0;
- public SystemDragDriver(DragController dragController, Intent dragIntent, DragView dragView) {
+ public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
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);
+ mDragObject = dragObject;
+ mContext = context;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- return !mIsFrameworkDragActive && super.onTouchEvent(ev);
+ return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- return !mIsFrameworkDragActive && super.onInterceptTouchEvent(ev);
+ return false;
}
@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) {
@@ -192,8 +139,6 @@ class SystemDragDriver extends DragDriver {
return true;
case DragEvent.ACTION_DRAG_ENTERED:
- mLastX = event.getX();
- mLastY = event.getY();
return true;
case DragEvent.ACTION_DRAG_LOCATION:
@@ -205,35 +150,66 @@ class SystemDragDriver extends DragDriver {
case DragEvent.ACTION_DROP:
mLastX = event.getX();
mLastY = event.getY();
- mReceivedDropEvent = true;
- return true;
+ mReceivedDropEvent =
+ updateInfoFromClipData(event.getClipData(), event.getClipDescription());
+ return mReceivedDropEvent;
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;
+ if (mReceivedDropEvent) {
+ mEventListener.onDriverDragEnd(mLastX, mLastY, null);
+ } else {
+ mEventListener.onDriverDragCancel();
+ }
return true;
default:
return false;
}
}
-};
+
+ private boolean updateInfoFromClipData(ClipData data, ClipDescription desc) {
+ if (data == null) {
+ return false;
+ }
+ ArrayList<Intent> intents = new ArrayList<>();
+ int itemCount = data.getItemCount();
+ for (int i = 0; i < itemCount; i++) {
+ Intent intent = data.getItemAt(i).getIntent();
+ if (intent == null) {
+ continue;
+ }
+
+ // Give preference to shortcut intents.
+ if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) {
+ intents.add(intent);
+ continue;
+ }
+ ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, intent);
+ if (info != null) {
+ mDragObject.dragInfo = info;
+ return true;
+ }
+ return true;
+ }
+
+ // Try creating shortcuts just using the intent and label
+ Intent fullIntent = new Intent().putExtra(Intent.EXTRA_SHORTCUT_NAME, desc.getLabel());
+ for (Intent intent : intents) {
+ fullIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
+ ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, fullIntent);
+ if (info != null) {
+ mDragObject.dragInfo = info;
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
/**
* Class for driving an internal (i.e. not using framework) drag/drop operation.
@@ -244,8 +220,5 @@ class InternalDragDriver extends DragDriver {
}
@Override
- public void onDragViewAnimationEnd() {}
-
- @Override
public boolean onDragEvent (DragEvent event) { return false; }
};
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index e88e77e20..016347b17 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -21,14 +21,22 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.content.ClipDescription;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -45,12 +53,14 @@ import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.PinchToOverviewListener;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -62,6 +72,7 @@ import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
+import java.net.URISyntaxException;
import java.util.ArrayList;
/**
@@ -100,7 +111,6 @@ public class DragLayer extends InsettableFrameLayout {
private TouchCompleteListener mTouchCompleteListener;
- private View mOverlayView;
private int mTopViewIndex;
private int mChildCountOnLastUpdate = -1;
@@ -172,20 +182,6 @@ public class DragLayer extends InsettableFrameLayout {
? null : new PinchToOverviewListener(mLauncher);
}
- public void showOverlayView(View overlayView) {
- LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- mOverlayView = overlayView;
- addView(overlayView, lp);
-
- // ensure that the overlay view stays on top. we can't use drawing order for this
- // because in API level 16 touch dispatch doesn't respect drawing order.
- mOverlayView.bringToFront();
- }
-
- public void dismissOverlayView() {
- removeView(mOverlayView);
- }
-
public boolean isEventOverPageIndicator(MotionEvent ev) {
getDescendantRectRelativeToSelf(mLauncher.getWorkspace().getPageIndicator(), mHitRect);
return mHitRect.contains((int) ev.getX(), (int) ev.getY());
@@ -358,16 +354,9 @@ public class DragLayer extends InsettableFrameLayout {
}
private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_FOCUSED);
- onInitializeAccessibilityEvent(event);
- event.getText().add(getContext().getString(stringId));
- accessibilityManager.sendAccessibilityEvent(event);
- }
+ int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
+ Utilities.sendCustomAccessibilityEvent(
+ this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
}
private boolean isInAccessibleDrag() {
@@ -377,37 +366,27 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder
- View topView = mLauncher.getOpenShortcutsContainer();
+ View topView = mLauncher.getTopFloatingView();
if (topView != null) {
- return handleTopViewSendAccessibilityEvent(topView, child, event);
- }
-
- topView = mLauncher.getWorkspace().getOpenFolder();
- if (topView != null) {
- return handleTopViewSendAccessibilityEvent(topView, child, event);
+ if (child == topView) {
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+ if (isInAccessibleDrag() && child instanceof DropTargetBar) {
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+ // Skip propagating onRequestSendAccessibilityEvent for all other children
+ // which are not topView
+ return false;
}
return super.onRequestSendAccessibilityEvent(child, event);
}
- private boolean handleTopViewSendAccessibilityEvent(
- View topView, View child, AccessibilityEvent event) {
- if (child == topView) {
- return super.onRequestSendAccessibilityEvent(child, event);
- }
- if (isInAccessibleDrag() && child instanceof DropTargetBar) {
- return super.onRequestSendAccessibilityEvent(child, event);
- }
- // Skip propagating onRequestSendAccessibilityEvent for all other children
- // which are not topView
- return false;
- }
-
@Override
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
- Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
- if (currentFolder != null) {
- // Only add the folder as a child for accessibility when it is open
- childrenForAccessibility.add(currentFolder);
+ View topView = mLauncher.getTopFloatingView();
+ if (topView != null) {
+ // Only add the top view as a child for accessibility when it is open
+ childrenForAccessibility.add(topView);
if (isInAccessibleDrag()) {
childrenForAccessibility.add(mLauncher.getDropTargetBar());
@@ -463,8 +442,46 @@ public class DragLayer extends InsettableFrameLayout {
return false;
}
+ @TargetApi(Build.VERSION_CODES.N)
+ private void handleSystemDragStart(DragEvent event) {
+ if (!FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER || !Utilities.isNycOrAbove()) {
+ return;
+ }
+ if (mLauncher.isWorkspaceLocked()) {
+ return;
+ }
+
+ ClipDescription description = event.getClipDescription();
+ if (!description.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+ return;
+ }
+ ShortcutInfo info = new ShortcutInfo();
+ // Set a dummy intent until we get the final value
+ info.intent = new Intent();
+
+ // Since we are not going through the workspace for starting the drag, set drag related
+ // information on the workspace before starting the drag.
+ ExternalDragPreviewProvider previewProvider =
+ new ExternalDragPreviewProvider(mLauncher, info);
+ mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
+
+ DragOptions options = new DragOptions();
+ options.systemDndStartPoint = new Point((int) event.getX(), (int) event.getY());
+
+ int halfPadding = previewProvider.previewPadding / 2;
+ mDragController.startDrag(
+ Bitmap.createBitmap(1, 1, Config.ARGB_8888),
+ 0, 0,
+ new AnotherWindowDragSource(mLauncher), info,
+ new Point(- halfPadding, halfPadding),
+ previewProvider.getPreviewBounds(), 1f, options);
+ }
+
@Override
public boolean onDragEvent (DragEvent event) {
+ if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+ handleSystemDragStart(event);
+ }
return mDragController.onDragEvent(event);
}
@@ -538,7 +555,9 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- return mDragController.dispatchUnhandledMove(focused, direction);
+ // Consume the unhandled move if a container is open, to avoid switching pages underneath.
+ boolean isContainerOpen = mLauncher.getTopFloatingView() != null;
+ return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction);
}
@Override
@@ -788,11 +807,15 @@ public class DragLayer extends InsettableFrameLayout {
// If duration < 0, this is a cue to compute the duration based on the distance
if (duration < 0) {
- duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
- if (dist < maxDist) {
- duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
+ if (mDragController != null && mDragController.isExternalDrag()) {
+ duration = 1;
+ } else {
+ duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
+ if (dist < maxDist) {
+ duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
+ }
+ duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
- duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
// Fall back to cubic ease out interpolator for the animation if none is specified
@@ -903,11 +926,6 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void onChildViewAdded(View parent, View child) {
super.onChildViewAdded(parent, child);
- if (mOverlayView != null) {
- // ensure that the overlay view stays on top. we can't use drawing order for this
- // because in API level 16 touch dispatch doesn't respect drawing order.
- mOverlayView.bringToFront();
- }
updateChildIndices();
}
@@ -919,11 +937,6 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void bringChildToFront(View child) {
super.bringChildToFront(child);
- if (child != mOverlayView && mOverlayView != null) {
- // ensure that the overlay view stays on top. we can't use drawing order for this
- // because in API level 16 touch dispatch doesn't respect drawing order.
- mOverlayView.bringToFront();
- }
updateChildIndices();
}
@@ -1062,6 +1075,26 @@ public class DragLayer extends InsettableFrameLayout {
return mBackgroundAlpha;
}
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ View topView = mLauncher.getTopFloatingView();
+ if (topView != null) {
+ return topView.requestFocus(direction, previouslyFocusedRect);
+ } else {
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ View topView = mLauncher.getTopFloatingView();
+ if (topView != null) {
+ topView.addFocusables(views, direction);
+ } else {
+ super.addFocusables(views, direction, focusableMode);
+ }
+ }
+
public void setTouchCompleteListener(TouchCompleteListener listener) {
mTouchCompleteListener = listener;
}
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
new file mode 100644
index 000000000..3d52a48c6
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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 android.graphics.Point;
+
+/**
+ * Set of options to control the drag and drop behavior.
+ */
+public class DragOptions {
+
+ /** Whether or not an accessible drag operation is in progress. */
+ public boolean isAccessibleDrag = false;
+
+ /** Specifies the start location for the system DnD, null when using internal DnD */
+ public Point systemDndStartPoint = null;
+}
diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
new file mode 100644
index 000000000..6b14be714
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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 android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} which provides a dummy outline when drag starts from
+ * a different window.
+ * It just draws an empty circle to a placeholder outline.
+ */
+public class ExternalDragPreviewProvider extends DragPreviewProvider {
+
+ private final Launcher mLauncher;
+ private final ItemInfo mAddInfo;
+
+ private final int[] mOutlineSize;
+
+ public ExternalDragPreviewProvider(Launcher launcher, ItemInfo addInfo) {
+ super(null);
+ mLauncher = launcher;
+ mAddInfo = addInfo;
+
+ mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false);
+ }
+
+ public Rect getPreviewBounds() {
+ Rect rect = new Rect();
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ rect.left = DRAG_BITMAP_PADDING / 2;
+ rect.top = (mOutlineSize[1] - dp.cellHeightPx) / 2;
+ rect.right = rect.left + dp.iconSizePx;
+ rect.bottom = rect.top + dp.iconSizePx;
+ return rect;
+ }
+
+ @Override
+ public Bitmap createDragOutline(Canvas canvas) {
+ final Bitmap b = Bitmap.createBitmap(mOutlineSize[0], mOutlineSize[1], Bitmap.Config.ALPHA_8);
+ canvas.setBitmap(b);
+
+ Paint paint = new Paint();
+ paint.setColor(Color.WHITE);
+ paint.setStyle(Paint.Style.FILL);
+
+ // Use 0.9f times the radius for the actual circle to account for icon normalization.
+ float radius = getPreviewBounds().width() * 0.5f;
+ canvas.drawCircle(DRAG_BITMAP_PADDING / 2 + radius,
+ DRAG_BITMAP_PADDING / 2 + radius, radius * 0.9f, paint);
+
+ HolographicOutlineHelper.obtain(mLauncher).applyExpensiveOutlineWithBlur(b, canvas);
+ canvas.setBitmap(null);
+ return b;
+ }
+}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 5450423a2..b64d12c1d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -42,7 +42,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
@@ -51,8 +50,8 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.Alarm;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.CellLayout.CellInfo;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -71,12 +70,12 @@ import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.accessibility.AccessibileDragListenerAdapter;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -92,7 +91,7 @@ import java.util.Comparator;
*/
public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
- View.OnFocusChangeListener, DragListener, DropTargetSource, AccessibilityDragSource {
+ View.OnFocusChangeListener, DragListener, DropTargetSource {
private static final String TAG = "Launcher.Folder";
/**
@@ -165,10 +164,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mRearrangeOnClose = false;
boolean mItemsInvalidated = false;
- private ShortcutInfo mCurrentDragInfo;
private View mCurrentDragView;
private boolean mIsExternalDrag;
- boolean mSuppressOnAdd = false;
private boolean mDragInProgress = false;
private boolean mDeleteFolderOnDropCompleted = false;
private boolean mSuppressFolderDeletion = false;
@@ -282,10 +279,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public boolean onLongClick(View v) {
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return true;
- return beginDrag(v, false);
+ return startDrag(v, new DragOptions());
}
- private boolean beginDrag(View v, boolean accessible) {
+ public boolean startDrag(View v, DragOptions options) {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
ShortcutInfo item = (ShortcutInfo) tag;
@@ -293,35 +290,55 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return false;
}
- mLauncher.getWorkspace().beginDragShared(v, this, accessible);
-
- mCurrentDragInfo = item;
mEmptyCellRank = item.rank;
mCurrentDragView = v;
- mContent.removeItem(mCurrentDragView);
- mInfo.remove(mCurrentDragInfo, true);
- mDragInProgress = true;
- mItemAddedBackToSelfViaIcon = false;
+ mDragController.addDragListener(this);
+ if (options.isAccessibleDrag) {
+ mDragController.addDragListener(new AccessibileDragListenerAdapter(
+ mContent, CellLayout.FOLDER_ACCESSIBILITY_DRAG) {
+
+ @Override
+ protected void enableAccessibleDrag(boolean enable) {
+ super.enableAccessibleDrag(enable);
+ mFooter.setImportantForAccessibility(enable
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
+ });
+ }
+
+ mLauncher.getWorkspace().beginDragShared(v, this, options);
}
return true;
}
@Override
- public void startDrag(CellInfo cellInfo, boolean accessible) {
- beginDrag(cellInfo.cell, accessible);
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ if (dragObject.dragSource != this) {
+ return;
+ }
+
+ mContent.removeItem(mCurrentDragView);
+ if (dragObject.dragInfo instanceof ShortcutInfo) {
+ mItemsInvalidated = true;
+
+ // We do not want to get events for the item being removed, as they will get handled
+ // when the drop completes
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.remove((ShortcutInfo) dragObject.dragInfo, true);
+ }
+ }
+ mDragInProgress = true;
+ mItemAddedBackToSelfViaIcon = false;
}
@Override
- public void enableAccessibleDrag(boolean enable) {
- mLauncher.getDropTargetBar().enableAccessibleDrag(enable);
- for (int i = 0; i < mContent.getChildCount(); i++) {
- mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
+ public void onDragEnd() {
+ if (mIsExternalDrag && mDragInProgress) {
+ completeDragExit();
}
-
- mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
- IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
+ mDragController.removeDragListener(this);
}
public boolean isEditingName() {
@@ -352,7 +369,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
LauncherModel.updateItemInDatabase(mLauncher, mInfo);
if (commit) {
- sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ Utilities.sendCustomAccessibilityEvent(
+ this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_renamed, newTitle));
}
@@ -591,7 +609,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
openFolderAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ Utilities.sendCustomAccessibilityEvent(
+ Folder.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
mState = STATE_ANIMATING;
}
@@ -650,9 +670,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
}
- public void beginExternalDrag(ShortcutInfo item) {
- mCurrentDragInfo = item;
- mEmptyCellRank = mContent.allocateRankForNewItem(item);
+ public void beginExternalDrag() {
+ mEmptyCellRank = mContent.allocateRankForNewItem();
mIsExternalDrag = true;
mDragInProgress = true;
@@ -661,28 +680,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mDragController.addDragListener(this);
}
- @Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) { }
-
- @Override
- public void onDragEnd() {
- if (mIsExternalDrag && mDragInProgress) {
- completeDragExit();
- }
- mDragController.removeDragListener(this);
- }
-
- @Thunk void sendCustomAccessibilityEvent(int type, String text) {
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(type);
- onInitializeAccessibilityEvent(event);
- event.getText().add(text);
- accessibilityManager.sendAccessibilityEvent(event);
- }
- }
-
public void animateClosed() {
if (!(getParent() instanceof DragLayer)) return;
final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
@@ -694,7 +691,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
@Override
public void onAnimationStart(Animator animation) {
- sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ Utilities.sendCustomAccessibilityEvent(
+ Folder.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
mState = STATE_ANIMATING;
}
@@ -851,9 +850,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
private void clearDragInfo() {
- mCurrentDragInfo = null;
mCurrentDragView = null;
- mSuppressOnAdd = false;
mIsExternalDrag = false;
}
@@ -917,9 +914,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mContent.arrangeChildren(views, views.size());
mItemsInvalidated = true;
- mSuppressOnAdd = true;
- mFolderIcon.onDrop(d);
- mSuppressOnAdd = false;
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mFolderIcon.onDrop(d);
+ }
}
if (target != this) {
@@ -936,9 +933,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mDeleteFolderOnDropCompleted = false;
mDragInProgress = false;
mItemAddedBackToSelfViaIcon = false;
- mCurrentDragInfo = null;
mCurrentDragView = null;
- mSuppressOnAdd = false;
// Reordering may have occured, and we need to save the new item locations. We do this once
// at the end to prevent unnecessary database operations.
@@ -1280,7 +1275,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mContent.completePendingPageChanges();
View currentDragView;
- ShortcutInfo si = mCurrentDragInfo;
+ final ShortcutInfo si;
+ if (d.dragInfo instanceof AppInfo) {
+ // Came from all apps -- make a copy.
+ si = ((AppInfo) d.dragInfo).makeShortcut();
+ } else {
+ // ShortcutInfo
+ si = (ShortcutInfo) d.dragInfo;
+ }
if (mIsExternalDrag) {
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
// Actually move the item in the database if it was an external drag. Call this
@@ -1317,11 +1319,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
rearrangeChildren();
// Temporarily suppress the listener, as we did all the work already here.
- mSuppressOnAdd = true;
- mInfo.add(si, false);
- mSuppressOnAdd = false;
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.add(si, false);
+ }
+
// Clear the drag info, as it is no longer being dragged.
- mCurrentDragInfo = null;
mDragInProgress = false;
if (mContent.getPageCount() > 1) {
@@ -1344,10 +1346,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public void onAdd(ShortcutInfo item) {
- // If the item was dropped onto this open folder, we have done the work associated
- // with adding the item to the folder, as indicated by mSuppressOnAdd being set
- if (mSuppressOnAdd) return;
- mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
+ mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
mItemsInvalidated = true;
LauncherModel.addOrMoveItemInDatabase(
mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
@@ -1355,9 +1354,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void onRemove(ShortcutInfo item) {
mItemsInvalidated = true;
- // If this item is being dragged from this open folder, we have already handled
- // the work associated with removing the item, so we don't have to do anything here.
- if (item == mCurrentDragInfo) return;
View v = getViewForInfo(item);
mContent.removeItem(v);
if (mState == STATE_ANIMATING) {
@@ -1496,4 +1492,20 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
}
};
+
+ /**
+ * Temporary resource held while we don't want to handle info changes
+ */
+ private class SuppressInfoChanges implements AutoCloseable {
+
+ SuppressInfoChanges() {
+ mInfo.removeListener(Folder.this);
+ }
+
+ @Override
+ public void close() {
+ mInfo.addListener(Folder.this);
+ updateTextViewFocus();
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index eebbfe8b7..69c2b0fa3 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -125,8 +125,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
Paint mBgPaint = new Paint();
private Alarm mOpenAlarm = new Alarm();
- @Thunk
- ItemInfo mDragInfo;
public FolderIcon(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -195,8 +193,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
return super.onSaveInstanceState();
}
-
-
public Folder getFolder() {
return mFolder;
}
@@ -242,22 +238,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
// Workspace#onDropExternal.
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
}
- mDragInfo = dragInfo;
}
OnAlarmListener mOnOpenListener = new OnAlarmListener() {
public void onAlarm(Alarm alarm) {
- ShortcutInfo item;
- if (mDragInfo instanceof AppInfo) {
- // Came from all apps -- make a copy.
- item = ((AppInfo) mDragInfo).makeShortcut();
- item.spanX = 1;
- item.spanY = 1;
- } else {
- // ShortcutInfo
- item = (ShortcutInfo) mDragInfo;
- }
- mFolder.beginExternalDrag(item);
+ mFolder.beginExternalDrag();
mLauncher.openFolder(FolderIcon.this);
}
};
@@ -284,7 +269,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
// This will animate the dragView (srcView) into the new folder
- onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
+ onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
}
public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
@@ -298,18 +283,13 @@ public class FolderIcon extends FrameLayout implements FolderListener {
onCompleteRunnable);
}
- public void onDragExit(Object dragInfo) {
- onDragExit();
- }
-
public void onDragExit() {
mBackground.animateToRest();
mOpenAlarm.cancelAlarm();
}
private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
- float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
- DragObject d) {
+ float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
item.cellX = -1;
item.cellY = -1;
@@ -379,7 +359,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
item = (ShortcutInfo) d.dragInfo;
}
mFolder.notifyDrop();
- onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
+ onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
}
private void computePreviewDrawingParams(int drawableSize, int totalSize) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3df1d2438..1171d488d 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -195,7 +195,7 @@ public class FolderPagedView extends PagedView {
* Create space for a new item at the end, and returns the rank for that item.
* Also sets the current page to the last page.
*/
- public int allocateRankForNewItem(ShortcutInfo info) {
+ public int allocateRankForNewItem() {
int rank = getItemCount();
ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
views.add(rank, null);
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index b90c2fd39..bc91c15be 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -27,8 +27,8 @@ import android.widget.TextView;
import com.android.launcher3.HolographicOutlineHelper;
import com.android.launcher3.Launcher;
import com.android.launcher3.PreloadIconDrawable;
-import com.android.launcher3.R;
import com.android.launcher3.Workspace;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.folder.FolderIcon;
/**
@@ -45,6 +45,8 @@ public class DragPreviewProvider {
// The padding added to the drag view during the preview generation.
public final int previewPadding;
+ public Bitmap gerenatedDragOutline;
+
public DragPreviewProvider(View view) {
mView = view;
@@ -118,19 +120,25 @@ public class DragPreviewProvider {
return b;
}
+ public final void generateDragOutline(Canvas canvas) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD && gerenatedDragOutline != null) {
+ throw new RuntimeException("Drag outline generated twice");
+ }
+
+ gerenatedDragOutline = createDragOutline(canvas);
+ }
+
/**
* Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
* Responsibility for the bitmap is transferred to the caller.
*/
public Bitmap createDragOutline(Canvas canvas) {
- final int outlineColor = mView.getResources().getColor(R.color.outline_color);
final Bitmap b = Bitmap.createBitmap(mView.getWidth() + DRAG_BITMAP_PADDING,
- mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ARGB_8888);
-
+ mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ALPHA_8);
canvas.setBitmap(b);
drawDragView(canvas);
HolographicOutlineHelper.obtain(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
+ .applyExpensiveOutlineWithBlur(b, canvas);
canvas.setBitmap(null);
return b;
}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index ddc9cbfd9..c86ba86fd 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -46,17 +46,12 @@ public class PackageItemInfo extends ItemInfo {
*/
public String titleSectionName;
- int flags = 0;
-
PackageItemInfo(String packageName) {
this.packageName = packageName;
}
@Override
- public String toString() {
- return "PackageItemInfo(title=" + title + " id=" + this.id
- + " type=" + this.itemType + " container=" + this.container
- + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
- + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
+ protected String dumpProperties() {
+ return super.dumpProperties() + " packageName=" + packageName;
}
}
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index b3f0c8203..0d7ba1e11 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -68,6 +68,17 @@ public class WidgetItem extends ComponentKey implements Comparable<WidgetItem> {
return thisWorkProfile ? 1 : -1;
}
- return sCollator.compare(label, another.label);
+ int labelCompare = sCollator.compare(label, another.label);
+ if (labelCompare != 0) {
+ return labelCompare;
+ }
+
+ // If the label is same, put the smaller widget before the larger widget. If the area is
+ // also same, put the widget with smaller height before.
+ int thisArea = spanX * spanY;
+ int otherArea = another.spanX * another.spanY;
+ return thisArea == otherArea
+ ? Integer.compare(spanY, another.spanY)
+ : Integer.compare(thisArea, otherArea);
}
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 747c21bbe..fb9d2f7fe 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -226,11 +226,6 @@ public class PageIndicatorDots extends PageIndicator {
public void setActiveMarker(int activePage) {
if (mActivePage != activePage) {
mActivePage = activePage;
-
- // Simulate a scroll change
- int totalScroll = mNumPages - 1;
- int currentScroll = mIsRtl ? (totalScroll - mActivePage) : mActivePage;
- setScroll(currentScroll, totalScroll);
}
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index ca3a2dd09..350bc8a9b 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -159,6 +159,11 @@ public class PageIndicatorLineCaret extends PageIndicator {
}
@Override
+ public void setContentDescription(CharSequence contentDescription) {
+ mAllAppsHandle.setContentDescription(contentDescription);
+ }
+
+ @Override
public void setScroll(int currentScroll, int totalScroll) {
if (getAlpha() == 0) {
return;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 9d8b6b3f4..47bee0669 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -24,6 +24,7 @@ import android.database.sqlite.SQLiteDatabase;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherProvider.DatabaseHelper;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
@@ -43,6 +44,13 @@ public class RestoreDbTask {
private static final String INFO_COLUMN_NAME = "name";
private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
+ /**
+ * When enabled all icons are kept on the home screen, even if they don't have an active
+ * session. To enable use:
+ * adb shell setprop log.tag.launcher_keep_all_icons VERBOSE
+ */
+ private static final String KEEP_ALL_ICONS = "launcher_keep_all_icons";
+
public static boolean performRestore(DatabaseHelper helper) {
SQLiteDatabase db = helper.getWritableDatabase();
db.beginTransaction();
@@ -77,15 +85,17 @@ public class RestoreDbTask {
}
// Mark all items as restored.
+ boolean keepAllIcons = Utilities.isPropertyEnabled(KEEP_ALL_ICONS);
ContentValues values = new ContentValues();
- values.put(Favorites.RESTORED, 1);
+ values.put(Favorites.RESTORED, ShortcutInfo.FLAG_RESTORED_ICON
+ | (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
db.update(Favorites.TABLE_NAME, values, null, null);
// Mark widgets with appropriate restore flag
- values.put(Favorites.RESTORED,
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
- LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
+ values.put(Favorites.RESTORED, LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+ LauncherAppWidgetInfo.FLAG_UI_NOT_READY |
+ (keepAllIcons ? LauncherAppWidgetInfo.FLAG_RESTORE_STARTED : 0));
db.update(Favorites.TABLE_NAME, values, "itemType = ?",
new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 37b6d0422..e7fc41512 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -21,16 +21,19 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer.UnbadgedShortcutInfo;
import com.android.launcher3.util.PillRevealOutlineProvider;
import com.android.launcher3.util.PillWidthRevealOutlineProvider;
@@ -48,6 +51,8 @@ public class DeepShortcutView extends FrameLayout implements ValueAnimator.Anima
private View mIconView;
private float mOpenAnimationProgress;
+ private UnbadgedShortcutInfo mInfo;
+
public DeepShortcutView(Context context) {
this(context, null, 0);
}
@@ -87,10 +92,36 @@ public class DeepShortcutView extends FrameLayout implements ValueAnimator.Anima
mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
- public void applyShortcutInfo(ShortcutInfo info) {
+ /** package private **/
+ void applyShortcutInfo(UnbadgedShortcutInfo info, DeepShortcutsContainer container) {
+ mInfo = info;
IconCache cache = LauncherAppState.getInstance().getIconCache();
mBubbleText.applyFromShortcutInfo(info, cache);
mIconView.setBackground(mBubbleText.getIcon());
+
+ // Use the long label as long as it exists and fits.
+ CharSequence longLabel = info.mDetail.getLongLabel();
+ int availableWidth = mBubbleText.getWidth() - mBubbleText.getTotalPaddingLeft()
+ - mBubbleText.getTotalPaddingRight();
+ boolean usingLongLabel = !TextUtils.isEmpty(longLabel)
+ && mBubbleText.getPaint().measureText(longLabel.toString()) <= availableWidth;
+ mBubbleText.setText(usingLongLabel ? longLabel : info.mDetail.getShortLabel());
+
+ // TODO: Add the click handler to this view directly and not the child view.
+ mBubbleText.setOnClickListener(Launcher.getLauncher(getContext()));
+ mBubbleText.setOnLongClickListener(container);
+ mBubbleText.setOnTouchListener(container);
+ }
+
+ /**
+ * Returns the shortcut info that is suitable to be added on the homescreen
+ */
+ public ShortcutInfo getFinalInfo() {
+ ShortcutInfo badged = new ShortcutInfo(mInfo);
+ // Queue an update task on the worker thread. This ensures that the badged
+ // shortcut eventually gets its icon updated.
+ Launcher.getLauncher(getContext()).getModel().updateShortcutInfo(mInfo.mDetail, badged);
+ return badged;
}
public View getIconView() {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 6bd21cd52..7657ed610 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -31,7 +31,6 @@ import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -53,6 +52,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherViewPropertyAnimator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
@@ -62,9 +62,9 @@ import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.graphics.TriangleShape;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -182,12 +182,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
}
for (int i = 0; i < shortcuts.size(); i++) {
final ShortcutInfoCompat shortcut = shortcuts.get(i);
- final ShortcutInfo launcherShortcutInfo =
- new UnbadgedShortcutInfo(shortcut, mLauncher);
- CharSequence shortLabel = shortcut.getShortLabel();
- CharSequence longLabel = shortcut.getLongLabel();
- uiHandler.post(new UpdateShortcutChild(i, launcherShortcutInfo,
- shortLabel, longLabel));
+ uiHandler.post(new UpdateShortcutChild(
+ i, new UnbadgedShortcutInfo(shortcut, mLauncher)));
}
}
});
@@ -196,32 +192,17 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
/** Updates the child of this container at the given index based on the given shortcut info. */
private class UpdateShortcutChild implements Runnable {
private int mShortcutChildIndex;
- private ShortcutInfo mShortcutChildInfo;
- private CharSequence mShortLabel;
- private CharSequence mLongLabel;
+ private UnbadgedShortcutInfo mShortcutChildInfo;
- public UpdateShortcutChild(int shortcutChildIndex, ShortcutInfo shortcutChildInfo,
- CharSequence shortLabel, CharSequence longLabel) {
+ public UpdateShortcutChild(int shortcutChildIndex, UnbadgedShortcutInfo shortcutChildInfo) {
mShortcutChildIndex = shortcutChildIndex;
mShortcutChildInfo = shortcutChildInfo;
- mShortLabel = shortLabel;
- mLongLabel = longLabel;
}
@Override
public void run() {
- DeepShortcutView shortcutViewContainer = getShortcutAt(mShortcutChildIndex);
- shortcutViewContainer.applyShortcutInfo(mShortcutChildInfo);
- BubbleTextView shortcutView = getShortcutAt(mShortcutChildIndex).getBubbleText();
- // Use the long label as long as it exists and fits.
- int availableWidth = shortcutView.getWidth() - shortcutView.getTotalPaddingLeft()
- - shortcutView.getTotalPaddingRight();
- boolean usingLongLabel = !TextUtils.isEmpty(mLongLabel)
- && shortcutView.getPaint().measureText(mLongLabel.toString()) <= availableWidth;
- shortcutView.setText(usingLongLabel ? mLongLabel : mShortLabel);
- shortcutView.setOnClickListener(mLauncher);
- shortcutView.setOnLongClickListener(DeepShortcutsContainer.this);
- shortcutView.setOnTouchListener(DeepShortcutsContainer.this);
+ getShortcutAt(mShortcutChildIndex)
+ .applyShortcutInfo(mShortcutChildInfo, DeepShortcutsContainer.this);
}
}
@@ -273,8 +254,10 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
@Override
public void onAnimationEnd(Animator animation) {
mOpenCloseAnimator = null;
-
- sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ Utilities.sendCustomAccessibilityEvent(
+ DeepShortcutsContainer.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ getContext().getString(R.string.action_deep_shortcut));
}
});
@@ -524,14 +507,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return false;
- UnbadgedShortcutInfo unbadgedInfo = (UnbadgedShortcutInfo) v.getTag();
- ShortcutInfo badged = new ShortcutInfo(unbadgedInfo);
- // Queue an update task on the worker thread. This ensures that the badged
- // shortcut eventually gets its icon updated.
- mLauncher.getModel().updateShortcutInfo(unbadgedInfo.mDetail, badged);
-
// Long clicked on a shortcut.
-
mDeferContainerRemoval = true;
DeepShortcutView sv = (DeepShortcutView) v.getParent();
sv.setWillDrawIcon(false);
@@ -541,8 +517,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
DragView dv = mLauncher.getWorkspace().beginDragShared(
- sv.getBubbleText(), this, false, badged,
- new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift));
+ sv.getBubbleText(), this, sv.getFinalInfo(),
+ new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions());
dv.animateShift(-mIconShift.x, -mIconShift.y);
// TODO: support dragging from within folder without having to close it
@@ -586,7 +562,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
}
@Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
// Either the original icon or one of the shortcuts was dragged.
// Hide the container, but don't remove it yet because that interferes with touch events.
animateClose();
@@ -603,8 +579,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
} else {
// Close animation is not running.
if (mDeferContainerRemoval) {
- mDeferContainerRemoval = false;
- mLauncher.getDragLayer().removeView(this);
+ close();
}
}
}
@@ -625,7 +600,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
mOpenCloseAnimator.cancel();
}
mIsOpen = false;
- mLauncher.getDragController().removeDragListener(this);
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
final int shortcutCount = getShortcutCount();
@@ -714,7 +688,9 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
mDeferContainerRemoval = false;
// Make the original icon visible in All Apps, but not in Workspace or Folders.
cleanupDeferredDrag(mDeferredDragIcon.getTag() instanceof AppInfo);
- mDeferredDragIcon.setTextVisibility(true);
+ boolean isInHotseat = ((ItemInfo) mDeferredDragIcon.getTag()).container
+ == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ mDeferredDragIcon.setTextVisibility(!isInHotseat);
mLauncher.getDragController().removeDragListener(this);
mLauncher.getDragLayer().removeView(this);
}
@@ -753,8 +729,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
/**
* Extension of {@link ShortcutInfo} which does not badge the icons.
*/
- private static class UnbadgedShortcutInfo extends ShortcutInfo {
- private final ShortcutInfoCompat mDetail;
+ static class UnbadgedShortcutInfo extends ShortcutInfo {
+ public final ShortcutInfoCompat mDetail;
public UnbadgedShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
super(shortcutInfo, context);
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index a25e475d4..2adb82e2d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -22,12 +22,9 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
-import android.widget.ImageView;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.HolographicOutlineHelper;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -45,23 +42,22 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider {
@Override
public Bitmap createDragOutline(Canvas canvas) {
- Bitmap b = drawScaledPreview(canvas);
+ Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ALPHA_8);
- final int outlineColor = mView.getResources().getColor(R.color.outline_color);
HolographicOutlineHelper.obtain(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
+ .applyExpensiveOutlineWithBlur(b, canvas);
canvas.setBitmap(null);
return b;
}
@Override
public Bitmap createDragBitmap(Canvas canvas) {
- Bitmap b = drawScaledPreview(canvas);
+ Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ARGB_8888);
canvas.setBitmap(null);
return b;
}
- private Bitmap drawScaledPreview(Canvas canvas) {
+ private Bitmap drawScaledPreview(Canvas canvas, Bitmap.Config config) {
Drawable d = mView.getBackground();
Rect bounds = getDrawableBounds(d);
@@ -70,7 +66,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider {
final Bitmap b = Bitmap.createBitmap(
size + DRAG_BITMAP_PADDING,
size + DRAG_BITMAP_PADDING,
- Bitmap.Config.ARGB_8888);
+ config);
canvas.setBitmap(b);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index ae552d284..6797c7ba3 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -139,13 +139,8 @@ public class LauncherExtension extends Launcher {
}
@Override
- public boolean providesSearch() {
- return false;
- }
-
- @Override
public boolean startSearch(String initialQuery, boolean selectInitialQuery,
- Bundle appSearchData, Rect sourceBounds) {
+ Bundle appSearchData) {
return false;
}
@@ -198,26 +193,6 @@ public class LauncherExtension extends Launcher {
}
@Override
- public Intent getFirstRunActivity() {
- return null;
- }
-
- @Override
- public boolean hasFirstRunActivity() {
- return false;
- }
-
- @Override
- public boolean hasDismissableIntroScreen() {
- return false;
- }
-
- @Override
- public View getIntroScreen() {
- return null;
- }
-
- @Override
public boolean shouldMoveToDefaultScreenOnHomeIntent() {
return true;
}
diff --git a/src/com/android/launcher3/util/ActivityResultInfo.java b/src/com/android/launcher3/util/ActivityResultInfo.java
new file mode 100644
index 000000000..2261bee8c
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityResultInfo.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Utility class which stores information from onActivityResult
+ */
+public class ActivityResultInfo implements Parcelable {
+
+ public final int requestCode;
+ public final int resultCode;
+ public final Intent data;
+
+ public ActivityResultInfo(int requestCode, int resultCode, Intent data) {
+ this.requestCode = requestCode;
+ this.resultCode = resultCode;
+ this.data = data;
+ }
+
+ private ActivityResultInfo(Parcel parcel) {
+ requestCode = parcel.readInt();
+ resultCode = parcel.readInt();
+ data = parcel.readInt() != 0 ? Intent.CREATOR.createFromParcel(parcel) : null;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(requestCode);
+ dest.writeInt(resultCode);
+ if (data != null) {
+ dest.writeInt(1);
+ data.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<ActivityResultInfo> CREATOR =
+ new Parcelable.Creator<ActivityResultInfo>() {
+ public ActivityResultInfo createFromParcel(Parcel source) {
+ return new ActivityResultInfo(source);
+ }
+
+ public ActivityResultInfo[] newArray(int size) {
+ return new ActivityResultInfo[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java
index 144b411fa..5882f217d 100644
--- a/src/com/android/launcher3/util/ComponentKey.java
+++ b/src/com/android/launcher3/util/ComponentKey.java
@@ -32,8 +32,8 @@ public class ComponentKey {
private final int mHashCode;
public ComponentKey(ComponentName componentName, UserHandleCompat user) {
- assert (componentName != null);
- assert (user != null);
+ Preconditions.assertNotNull(componentName);
+ Preconditions.assertNotNull(user);
this.componentName = componentName;
this.user = user;
mHashCode = Arrays.hashCode(new Object[] {componentName, user});
@@ -58,6 +58,8 @@ public class ComponentKey {
componentName = ComponentName.unflattenFromString(componentKeyStr);
user = UserHandleCompat.myUserHandle();
}
+ Preconditions.assertNotNull(componentName);
+ Preconditions.assertNotNull(user);
mHashCode = Arrays.hashCode(new Object[] {componentName, user});
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 3c4c79aec..3e15d05e1 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,10 +16,15 @@
package com.android.launcher3.util;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.text.TextUtils;
import com.android.launcher3.Utilities;
@@ -96,4 +101,53 @@ public class PackageManagerHelper {
}
return excludePackages.get(0);
}
+
+ /**
+ * Returns true if {@param srcPackage} has the permission required to start the activity from
+ * {@param intent}. If {@param srcPackage} is null, then the activity should not need
+ * any permissions
+ */
+ public static boolean hasPermissionForActivity(Context context, Intent intent,
+ String srcPackage) {
+ PackageManager pm = context.getPackageManager();
+ ResolveInfo target = pm.resolveActivity(intent, 0);
+ if (target == null) {
+ // Not a valid target
+ return false;
+ }
+ if (TextUtils.isEmpty(target.activityInfo.permission)) {
+ // No permission is needed
+ return true;
+ }
+ if (TextUtils.isEmpty(srcPackage)) {
+ // The activity requires some permission but there is no source.
+ return false;
+ }
+
+ // Source does not have sufficient permissions.
+ if(pm.checkPermission(target.activityInfo.permission, srcPackage) !=
+ PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ if (!Utilities.ATLEAST_MARSHMALLOW) {
+ // These checks are sufficient for below M devices.
+ return true;
+ }
+
+ // On M and above also check AppOpsManager for compatibility mode permissions.
+ if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
+ // There is no app-op for this permission, which could have been disabled.
+ return true;
+ }
+
+ // There is no direct way to check if the app-op is allowed for a particular app. Since
+ // app-op is only enabled for apps running in compatibility mode, simply block such apps.
+
+ try {
+ return pm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
+ } catch (NameNotFoundException e) { }
+
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
new file mode 100644
index 000000000..4e402f348
--- /dev/null
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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.content.ContentValues;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+
+/**
+ * Utility class to store information regarding a pending request made by launcher. This information
+ * can be saved across launcher instances.
+ */
+public class PendingRequestArgs extends ItemInfo implements Parcelable {
+
+ private static final int TYPE_NONE = 0;
+ private static final int TYPE_INTENT = 1;
+ private static final int TYPE_APP_WIDGET = 2;
+
+ private final int mArg1;
+ private final int mObjectType;
+ private final Parcelable mObject;
+
+ public PendingRequestArgs(ItemInfo info) {
+ mArg1 = 0;
+ mObjectType = TYPE_NONE;
+ mObject = null;
+
+ copyFrom(info);
+ }
+
+ private PendingRequestArgs(int arg1, int objectType, Parcelable object) {
+ mArg1 = arg1;
+ mObjectType = objectType;
+ mObject = object;
+ }
+
+ public PendingRequestArgs(Parcel parcel) {
+ readFromValues(ContentValues.CREATOR.createFromParcel(parcel));
+
+ mArg1 = parcel.readInt();
+ mObjectType = parcel.readInt();
+ if (parcel.readInt() != 0) {
+ mObject = mObjectType == TYPE_INTENT
+ ? Intent.CREATOR.createFromParcel(parcel)
+ : new LauncherAppWidgetProviderInfo(parcel);
+ } else {
+ mObject = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ ContentValues itemValues = new ContentValues();
+ writeToValues(itemValues);
+
+ dest.writeInt(mArg1);
+ dest.writeInt(mObjectType);
+ if (mObject != null) {
+ dest.writeInt(1);
+ mObject.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public LauncherAppWidgetProviderInfo getWidgetProvider() {
+ return mObjectType == TYPE_APP_WIDGET ? (LauncherAppWidgetProviderInfo) mObject : null;
+ }
+
+ public int getWidgetId() {
+ return mObjectType == TYPE_APP_WIDGET ? mArg1 : 0;
+ }
+
+ public Intent getPendingIntent() {
+ return mObjectType == TYPE_INTENT ? (Intent) mObject : null;
+ }
+
+ public int getRequestCode() {
+ return mObjectType == TYPE_INTENT ? mArg1 : 0;
+ }
+
+ public static PendingRequestArgs forWidgetInfo(
+ int appWidgetId, LauncherAppWidgetProviderInfo widgetInfo, ItemInfo info) {
+ PendingRequestArgs args = new PendingRequestArgs(appWidgetId, TYPE_APP_WIDGET, widgetInfo);
+ args.copyFrom(info);
+ return args;
+ }
+
+ public static PendingRequestArgs forIntent(int requestCode, Intent intent, ItemInfo info) {
+ PendingRequestArgs args = new PendingRequestArgs(requestCode, TYPE_INTENT, intent);
+ args.copyFrom(info);
+ return args;
+ }
+
+ public static final Parcelable.Creator<PendingRequestArgs> CREATOR =
+ new Parcelable.Creator<PendingRequestArgs>() {
+ public PendingRequestArgs createFromParcel(Parcel source) {
+ return new PendingRequestArgs(source);
+ }
+
+ public PendingRequestArgs[] newArray(int size) {
+ return new PendingRequestArgs[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index 3760c6372..89353e110 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -26,6 +26,12 @@ import com.android.launcher3.config.ProviderConfig;
*/
public class Preconditions {
+ public static void assertNotNull(Object o) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD && o == null) {
+ throw new IllegalStateException();
+ }
+ }
+
public static void assertWorkerThread() {
if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
throw new IllegalStateException();
diff --git a/src/com/android/launcher3/util/VerticalFlingDetector.java b/src/com/android/launcher3/util/VerticalFlingDetector.java
new file mode 100644
index 000000000..7236c2d1b
--- /dev/null
+++ b/src/com/android/launcher3/util/VerticalFlingDetector.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 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.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public class VerticalFlingDetector implements View.OnTouchListener {
+
+ private static final float CUSTOM_SLOP_MULTIPLIER = 2.2f;
+ private static final int SEC_IN_MILLIS = 1000;
+
+ private VelocityTracker mVelocityTracker;
+ private float mMinimumFlingVelocity;
+ private float mMaximumFlingVelocity;
+ private float mDownX, mDownY;
+ private boolean mShouldCheckFling;
+ private double mCustomTouchSlop;
+
+ public VerticalFlingDetector(Context context) {
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mMinimumFlingVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaximumFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ mCustomTouchSlop = CUSTOM_SLOP_MULTIPLIER * vc.getScaledTouchSlop();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownX = ev.getX();
+ mDownY = ev.getY();
+ mShouldCheckFling = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mShouldCheckFling) {
+ break;
+ }
+ if (Math.abs(ev.getY() - mDownY) > mCustomTouchSlop &&
+ Math.abs(ev.getY() - mDownY) > Math.abs(ev.getX() - mDownX)) {
+ mShouldCheckFling = true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mShouldCheckFling) {
+ mVelocityTracker.computeCurrentVelocity(SEC_IN_MILLIS, mMaximumFlingVelocity);
+ // only when fling is detected in down direction
+ if (mVelocityTracker.getYVelocity() > mMinimumFlingVelocity) {
+ cleanUp();
+ return true;
+ }
+ }
+ // fall through.
+ case MotionEvent.ACTION_CANCEL:
+ cleanUp();
+ }
+ return false;
+ }
+
+ private void cleanUp() {
+ if (mVelocityTracker == null) {
+ return;
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index a56985083..486b18ef2 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -35,10 +35,4 @@ public class PendingAddShortcutInfo extends PendingAddItemInfo {
componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
-
- @Override
- public String toString() {
- return String.format("PendingAddShortcutInfo package=%s, name=%s",
- activityInfo.packageName, activityInfo.name);
- }
}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index de06ab664..f800ac44d 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -59,10 +59,4 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo {
public boolean isCustomWidget() {
return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
}
-
- @Override
- public String toString() {
- return String.format("PendingAddWidgetInfo package=%s, name=%s",
- componentName.getPackageName(), componentName.getShortClassName());
- }
}
diff --git a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
new file mode 100644
index 000000000..eaa0bb3d5
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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.widget;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
+ * dragged from the widget tray.
+ */
+public class PendingItemPreviewProvider extends DragPreviewProvider {
+
+ private final PendingAddItemInfo mAddInfo;
+ private final Bitmap mPreviewBitmap;
+
+ public PendingItemPreviewProvider(View view, PendingAddItemInfo addInfo, Bitmap preview) {
+ super(view);
+ mAddInfo = addInfo;
+ mPreviewBitmap = preview;
+ }
+
+ @Override
+ public Bitmap createDragOutline(Canvas canvas) {
+ Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace();
+ int[] size = workspace.estimateItemSize(mAddInfo, false);
+
+ int w = size[0];
+ int h = size[1];
+ final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
+ canvas.setBitmap(b);
+
+ Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
+ float scaleFactor = Math.min((w - DRAG_BITMAP_PADDING) / (float) mPreviewBitmap.getWidth(),
+ (h - DRAG_BITMAP_PADDING) / (float) mPreviewBitmap.getHeight());
+ int scaledWidth = (int) (scaleFactor * mPreviewBitmap.getWidth());
+ int scaledHeight = (int) (scaleFactor * mPreviewBitmap.getHeight());
+ Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
+
+ // center the image
+ dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
+
+ canvas.drawBitmap(mPreviewBitmap, src, dst, null);
+
+ // Don't clip alpha values for the drag outline if we're using the default widget preview
+ boolean clipAlpha = !(mAddInfo instanceof PendingAddWidgetInfo &&
+ (((PendingAddWidgetInfo) mAddInfo).previewImage == 0));
+ HolographicOutlineHelper.obtain(mView.getContext())
+ .applyExpensiveOutlineWithBlur(b, canvas, clipAlpha);
+ canvas.setBitmap(null);
+
+ return b;
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 97877fd35..293585dd6 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -135,6 +135,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
mWidgetName.setText(mItem.label);
mWidgetDims.setText(getContext().getString(R.string.widget_dims_format,
mItem.spanX, mItem.spanY));
+ mWidgetDims.setContentDescription(getContext().getString(
+ R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
mWidgetPreviewLoader = loader;
if (item.activityInfo != null) {
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 297505be2..049871f98 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -11,6 +11,7 @@ import android.view.View;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -18,6 +19,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.util.Thunk;
public class WidgetHostViewLoader implements DragController.DragListener {
@@ -47,7 +49,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
}
@Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) { }
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
@Override
public void onDragEnd() {
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 538d4c988..89c44c859 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -33,6 +33,7 @@ import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.IconCache;
import com.android.launcher3.ItemInfo;
@@ -48,6 +49,7 @@ import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TransformingTouchDelegate;
/**
* The widgets list view container.
@@ -63,11 +65,11 @@ public class WidgetsContainerView extends BaseContainerView
private IconCache mIconCache;
private final Rect mTmpBgPaddingRect = new Rect();
- private final Rect mTmpRect = new Rect();
/* Recycler view related member variables */
private WidgetsRecyclerView mRecyclerView;
private WidgetsListAdapter mAdapter;
+ private TransformingTouchDelegate mRecyclerViewTouchDelegate;
/* Touch handling related member variables. */
private Toast mWidgetInstructionToast;
@@ -95,29 +97,29 @@ public class WidgetsContainerView extends BaseContainerView
}
@Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ getRevealView().getBackground().getPadding(mTmpBgPaddingRect);
+ mRecyclerViewTouchDelegate.setBounds(
+ mRecyclerView.getLeft() - mTmpBgPaddingRect.left,
+ mRecyclerView.getTop() - mTmpBgPaddingRect.top,
+ mRecyclerView.getRight() + mTmpBgPaddingRect.right,
+ mRecyclerView.getBottom() + mTmpBgPaddingRect.bottom);
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mRecyclerViewTouchDelegate = new TransformingTouchDelegate(mRecyclerView);
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- getRevealView().getBackground().getPadding(mTmpBgPaddingRect);
- if (Utilities.isRtl(getResources())) {
- getContentView().setPadding(0, mTmpBgPaddingRect.top, mTmpBgPaddingRect.right,
- mTmpBgPaddingRect.bottom);
- mTmpRect.set(mTmpBgPaddingRect.left, 0, 0, 0);
- mRecyclerView.updateBackgroundPadding(mTmpRect);
- } else {
- getContentView().setPadding(mTmpBgPaddingRect.left, mTmpBgPaddingRect.top, 0,
- mTmpBgPaddingRect.bottom);
- mTmpRect.set(0, 0, mTmpBgPaddingRect.right, 0);
- mRecyclerView.updateBackgroundPadding(mTmpRect);
- }
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ((View) mRecyclerView.getParent()).setTouchDelegate(mRecyclerViewTouchDelegate);
}
//
@@ -207,7 +209,7 @@ public class WidgetsContainerView extends BaseContainerView
// Compose the drag image
Bitmap preview;
- float scale = 1f;
+ final float scale;
final Rect bounds = image.getBitmapBounds();
if (createItemInfo instanceof PendingAddWidgetInfo) {
@@ -244,19 +246,14 @@ public class WidgetsContainerView extends BaseContainerView
scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth();
}
- // Don't clip alpha values for the drag outline if we're using the default widget preview
- boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
- (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
+ // Since we are not going through the workspace for starting the drag, set drag related
+ // information on the workspace before starting the drag.
+ mLauncher.getWorkspace().prepareDragWithProvider(
+ new PendingItemPreviewProvider(v, createItemInfo, preview));
// Start the drag
- mLauncher.lockScreenOrientation();
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();
+ bounds, scale, new DragOptions());
return true;
}