diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2017-01-20 19:32:31 -0800 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2017-01-24 22:24:54 -0800 |
commit | b38fab75735c601b61765ecae61494c8637aaef4 (patch) | |
tree | 02ccb88b0137bf2f5a93db052ae0f4f11983bdf9 /src/com | |
parent | 658058b960ef029fc70f0f3e131057a6ba0d10e2 (diff) | |
download | android_packages_apps_Trebuchet-b38fab75735c601b61765ecae61494c8637aaef4.tar.gz android_packages_apps_Trebuchet-b38fab75735c601b61765ecae61494c8637aaef4.tar.bz2 android_packages_apps_Trebuchet-b38fab75735c601b61765ecae61494c8637aaef4.zip |
Adding support for drag and drop for requestPinItem.
On long pressing, the confirmation activity starts a system
drag-n-drop and focuses the launcher activity. We then drive
the launcher drag controller using the system drag event
Caveats:
> We use a transparent preview for system drag and drop and use
a view inside launcher for actual preview. This gives us better
control over various animations.
> The parameters for drag operation are passed to the Launcher
activity using the intent. Since onNewIntent and onDragEvent
come at different times and are not associated, a random uuid
is used as mime-type to match the drag event with intent params
> If the workspace is locked (eg, loader is running) the drag
operation is simply dropped. Will be imporved in follow up cls
Bug: 33584624
Change-Id: I0bb5b25b690f86b6af31a14e11beb669fcb3a281
Diffstat (limited to 'src/com')
14 files changed, 411 insertions, 342 deletions
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java deleted file mode 100644 index 052e5d09a..000000000 --- a/src/com/android/launcher3/AnotherWindowDropTarget.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3; - -import android.content.Context; -import android.graphics.PointF; -import android.graphics.Rect; - -/** - * Drop target used when another window (i.e. another process) has accepted a global system drag. - * If the accepted item was a shortcut, we delete it from Launcher. - */ -public class AnotherWindowDropTarget implements DropTarget { - final Launcher mLauncher; - - public AnotherWindowDropTarget (Context context) { mLauncher = Launcher.getLauncher(context); } - - @Override - public boolean isDropEnabled() { return true; } - - @Override - public void onDrop(DragObject dragObject) { - dragObject.deferDragViewCleanupPostAnimation = false; - LauncherModel.deleteItemFromDatabase(mLauncher, (ShortcutInfo) dragObject.dragInfo); - } - - @Override - public void onDragEnter(DragObject dragObject) {} - - @Override - public void onDragOver(DragObject dragObject) {} - - @Override - public void onDragExit(DragObject dragObject) {} - - @Override - public boolean acceptDrop(DragObject dragObject) { - return dragObject.dragInfo instanceof ShortcutInfo; - } - - @Override - public void prepareAccessibilityDrop() {} - - // These methods are implemented in Views - @Override - public void getHitRectRelativeToDragLayer(Rect outRect) {} -} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8d8a70c0f..8dfc211a9 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -49,6 +49,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; @@ -94,6 +95,7 @@ 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.dragndrop.PinItemDragListener; import com.android.launcher3.dynamicui.ExtractedColors; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; @@ -826,7 +828,7 @@ public class Launcher extends BaseActivity } @Override - protected void onActivityResult( + public void onActivityResult( final int requestCode, final int resultCode, final Intent data) { handleActivityResult(requestCode, resultCode, data); if (mLauncherCallbacks != null) { @@ -1752,6 +1754,14 @@ public class Launcher extends BaseActivity if (mLauncherCallbacks != null) { mLauncherCallbacks.onHomeIntent(); } + + Parcelable dragExtra = intent + .getParcelableExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER); + if (dragExtra instanceof PinItemDragListener) { + PinItemDragListener dragListener = (PinItemDragListener) dragExtra; + dragListener.setLauncher(this); + mDragLayer.setOnDragListener(dragListener); + } } if (mLauncherCallbacks != null) { diff --git a/src/com/android/launcher3/compat/PinItemRequestCompat.java b/src/com/android/launcher3/compat/PinItemRequestCompat.java index 74d77659c..481310a15 100644 --- a/src/com/android/launcher3/compat/PinItemRequestCompat.java +++ b/src/com/android/launcher3/compat/PinItemRequestCompat.java @@ -21,13 +21,14 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.os.Bundle; +import android.os.Parcel; import android.os.Parcelable; /** * A wrapper around platform implementation of PinItemRequestCompat until the * updated SDK is available. */ -public class PinItemRequestCompat { +public class PinItemRequestCompat implements Parcelable { public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST"; @@ -83,6 +84,32 @@ public class PinItemRequestCompat { } } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeParcelable(mObject, i); + } + + public Intent toIntent() { + return new Intent().putExtra(EXTRA_PIN_ITEM_REQUEST, mObject); + } + + public static final Parcelable.Creator<PinItemRequestCompat> CREATOR = + new Parcelable.Creator<PinItemRequestCompat>() { + public PinItemRequestCompat createFromParcel(Parcel source) { + Parcelable object = source.readParcelable(null); + return new PinItemRequestCompat(object); + } + + public PinItemRequestCompat[] newArray(int size) { + return new PinItemRequestCompat[size]; + } + }; + public static PinItemRequestCompat getPinItemRequest(Intent intent) { Parcelable extra = intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST); return extra == null ? null : new PinItemRequestCompat(extra); diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java index ece775986..1cfbd2061 100644 --- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java +++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java @@ -35,6 +35,7 @@ import android.util.Log; import android.widget.Toast; import com.android.launcher3.IconCache; +import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -67,7 +68,7 @@ public abstract class ShortcutConfigActivityInfo { public abstract Drawable getFullResIcon(IconCache cache); - public boolean startConfigActivity(Activity activity, int requestCode) { + public boolean startConfigActivity(Launcher activity, int requestCode) { Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT) .setComponent(getComponent()); try { @@ -136,7 +137,7 @@ public abstract class ShortcutConfigActivityInfo { } @Override - public boolean startConfigActivity(Activity activity, int requestCode) { + public boolean startConfigActivity(Launcher activity, int requestCode) { if (getUser().equals(Process.myUserHandle())) { return super.startConfigActivity(activity, requestCode); } diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 6c6f14124..423fdabfc 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -17,12 +17,23 @@ package com.android.launcher3.dragndrop; import android.annotation.TargetApi; +import android.app.ActivityOptions; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; +import android.content.ClipData; +import android.content.ClipDescription; import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; import android.os.Build; import android.os.Bundle; +import android.util.Log; +import android.view.MotionEvent; import android.view.View; +import android.view.View.*; import com.android.launcher3.BaseActivity; import com.android.launcher3.InstallShortcutReceiver; @@ -38,13 +49,18 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetCell; import com.android.launcher3.widget.WidgetHostViewLoader; +import com.android.launcher3.widget.WidgetImageView; @TargetApi(Build.VERSION_CODES.N_MR1) -public class AddItemActivity extends BaseActivity { +public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener { + + private static final int SHADOW_SIZE = 10; private static final int REQUEST_BIND_APPWIDGET = 1; private static final String STATE_EXTRA_WIDGET_ID = "state.widget.id"; + private final PointF mLastTouchPos = new PointF(); + private PinItemRequestCompat mRequest; private LauncherAppState mApp; private InvariantDeviceProfile mIdp; @@ -86,11 +102,54 @@ public class AddItemActivity extends BaseActivity { finish(); } } + + mWidgetCell.setOnTouchListener(this); + mWidgetCell.setOnLongClickListener(this); + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + mLastTouchPos.set(motionEvent.getX(), motionEvent.getY()); + return false; + } + + @Override + public boolean onLongClick(View view) { + // Find the position of the preview relative to the touch location. + WidgetImageView img = mWidgetCell.getWidgetView(); + Rect bounds = img.getBitmapBounds(); + bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y); + + // Start home and pass the draw request params + PinItemDragListener listener = new PinItemDragListener(mRequest, bounds); + Intent homeIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setPackage(getPackageName()) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER, listener); + startActivity(homeIntent, + ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle()); + + // Start a system drag and drop. We use a transparent bitmap as preview for system drag + // as the preview is handled internally by launcher. + ClipDescription description = new ClipDescription("", new String[]{listener.getMimeType()}); + ClipData data = new ClipData(description, new ClipData.Item("")); + view.startDragAndDrop(data, new DragShadowBuilder(view) { + + @Override + public void onDrawShadow(Canvas canvas) { } + + @Override + public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) { + outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE); + outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2); + } + }, null, View.DRAG_FLAG_GLOBAL); + return false; } private void setupShortcut() { - WidgetItem item = new WidgetItem(new PinShortcutRequestActivityInfo( - mRequest.getShortcutInfo(), this)); + WidgetItem item = new WidgetItem(new PinShortcutRequestActivityInfo(mRequest, this)); mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache()); mWidgetCell.ensurePreview(); } diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java deleted file mode 100644 index 162301069..000000000 --- a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 supportsAppInfoDropTarget() { - return false; - } - - @Override - public boolean supportsDeleteDropTarget() { - return false; - } - - @Override - public float getIntrinsicIconScaleFactor() { - return 1; - } - - @Override - public void onDropCompleted(View target, DragObject d, - boolean isFlingToDelete, boolean success) { - if (!success) { - Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null); - } - - } - - @Override - public void fillInLogContainerData(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 745776db3..80c286027 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -36,7 +36,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.accessibility.DragViewStateAnnouncer; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.TouchController; @@ -447,7 +446,8 @@ public class DragController implements DragDriver.EventListener, TouchController /** * Call this from a drag source view. */ - public boolean onDragEvent(DragEvent event) { + public boolean onDragEvent(long dragStartTime, DragEvent event) { + mFlingToDeleteHelper.recordDragEvent(dragStartTime, event); return mDragDriver != null && mDragDriver.onDragEvent(event); } diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java index a90cfffdc..65c0f29b1 100644 --- a/src/com/android/launcher3/dragndrop/DragDriver.java +++ b/src/com/android/launcher3/dragndrop/DragDriver.java @@ -16,20 +16,13 @@ package com.android.launcher3.dragndrop; -import android.content.ClipData; -import android.content.ClipDescription; import android.content.Context; -import android.content.Intent; import android.view.DragEvent; import android.view.MotionEvent; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.InstallShortcutReceiver; -import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; -import java.util.ArrayList; - /** * Base class for driving a drag/drop operation. */ @@ -107,7 +100,6 @@ class SystemDragDriver extends DragDriver { private final DragObject mDragObject; private final Context mContext; - boolean mReceivedDropEvent = false; float mLastX = 0; float mLastY = 0; @@ -149,65 +141,21 @@ class SystemDragDriver extends DragDriver { case DragEvent.ACTION_DROP: mLastX = event.getX(); mLastY = event.getY(); - mReceivedDropEvent = - updateInfoFromClipData(event.getClipData(), event.getClipDescription()); - return mReceivedDropEvent; - + mEventListener.onDriverDragMove(event.getX(), event.getY()); + mEventListener.onDriverDragEnd(mLastX, mLastY); + return true; case DragEvent.ACTION_DRAG_EXITED: mEventListener.onDriverDragExitWindow(); return true; case DragEvent.ACTION_DRAG_ENDED: - if (mReceivedDropEvent) { - mEventListener.onDriverDragEnd(mLastX, mLastY); - } else { - mEventListener.onDriverDragCancel(); - } + 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; - } } /** diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 4279cc319..de416e3bc 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -21,21 +21,13 @@ 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.os.Build; import android.util.AttributeSet; -import android.view.DragEvent; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -59,9 +51,7 @@ 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; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; @@ -373,49 +363,6 @@ 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.ATLEAST_NOUGAT) { - 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); - } - /** * Determine the rect of the descendant in this DragLayer's coordinates * diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java deleted file mode 100644 index e558487b8..000000000 --- a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.graphics.DragPreviewProvider; -import com.android.launcher3.graphics.HolographicOutlineHelper; - -/** - * 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, launcher); - mLauncher = launcher; - mAddInfo = addInfo; - - mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false, false); - } - - public Rect getPreviewBounds() { - Rect rect = new Rect(); - DeviceProfile dp = mLauncher.getDeviceProfile(); - rect.left = blurSizeOutline / 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(blurSizeOutline / 2 + radius, - blurSizeOutline / 2 + radius, radius * 0.9f, paint); - - HolographicOutlineHelper.getInstance(mLauncher).applyExpensiveOutlineWithBlur(b, canvas); - canvas.setBitmap(null); - return b; - } -} diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java index a2aa27d17..e79474483 100644 --- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java +++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java @@ -17,6 +17,8 @@ package com.android.launcher3.dragndrop; import android.graphics.PointF; +import android.os.SystemClock; +import android.view.DragEvent; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; @@ -53,6 +55,31 @@ public class FlingToDeleteHelper { mVelocityTracker.addMovement(ev); } + /** + * Same as {@link #recordMotionEvent}. It creates a temporary {@link MotionEvent} object + * using {@param event} for tracking velocity. + */ + public void recordDragEvent(long dragStartTime, DragEvent event) { + final int motionAction; + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + motionAction = MotionEvent.ACTION_DOWN; + break; + case DragEvent.ACTION_DRAG_LOCATION: + motionAction = MotionEvent.ACTION_MOVE; + break; + case DragEvent.ACTION_DRAG_ENDED: + motionAction = MotionEvent.ACTION_UP; + break; + default: + return; + } + MotionEvent emulatedEvent = MotionEvent.obtain(dragStartTime, SystemClock.uptimeMillis(), + motionAction, event.getX(), event.getY(), 0); + recordMotionEvent(emulatedEvent); + emulatedEvent.recycle(); + } + public void releaseVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java new file mode 100644 index 000000000..1a99cc886 --- /dev/null +++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2017 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.ClipDescription; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.Log; +import android.view.DragEvent; +import android.view.View; + +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.PendingAddItemInfo; +import com.android.launcher3.compat.PinItemRequestCompat; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.graphics.LauncherIcons; +import com.android.launcher3.shortcuts.ShortcutInfoCompat; +import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.widget.PendingAddShortcutInfo; +import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.launcher3.widget.PendingItemPreviewProvider; + +import java.util.UUID; + +/** + * {@link DragSource} for handling drop from from a different window. This object is initialized + * in the source window and is passed on to the Launcher activity as an Intent extra. + */ +public class PinItemDragListener implements Parcelable, View.OnDragListener, DragSource { + + private static final String TAG = "PinItemDragListener"; + + private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/"; + public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener"; + + private final PinItemRequestCompat mRequest; + + // Position of preview relative to the touch location + private final Rect mPreviewRect; + + // Randomly generated id used to verify the drag event. + private final String mId; + + private Launcher mLauncher; + private DragController mDragController; + private long mDragStartTime; + + public PinItemDragListener(PinItemRequestCompat request, Rect previewRect) { + mRequest = request; + mPreviewRect = previewRect; + mId = UUID.randomUUID().toString(); + } + + private PinItemDragListener(Parcel parcel) { + mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel); + mPreviewRect = Rect.CREATOR.createFromParcel(parcel); + mId = parcel.readString(); + } + + public String getMimeType() { + return MIME_TYPE_PREFIX + mId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + mRequest.writeToParcel(parcel, i); + mPreviewRect.writeToParcel(parcel, i); + parcel.writeString(mId); + } + + public void setLauncher(Launcher launcher) { + mLauncher = launcher; + mDragController = launcher.getDragController(); + } + + @Override + public boolean onDrag(View view, DragEvent event) { + if (mLauncher == null || mDragController == null) { + postCleanup(); + return false; + } + if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) { + if (onDragStart(event)) { + return true; + } else { + postCleanup(); + return false; + } + } + return mDragController.onDragEvent(mDragStartTime, event); + } + + private boolean onDragStart(DragEvent event) { + if (!mRequest.isValid()) { + return false; + } + ClipDescription desc = event.getClipDescription(); + if (desc == null || !desc.hasMimeType(getMimeType())) { + Log.e(TAG, "Someone started a dragAndDrop before us."); + return false; + } + + if (mLauncher.isWorkspaceLocked()) { + // TODO: implement wait + return false; + } + + final PendingAddItemInfo item; + final Bitmap preview; + + Point dragShift = new Point(mPreviewRect.left, mPreviewRect.top); + if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) { + item = new PendingAddShortcutInfo( + new PinShortcutRequestActivityInfo(mRequest, mLauncher)); + + ShortcutInfoCompat compat = new ShortcutInfoCompat(mRequest.getShortcutInfo()); + Bitmap icon = LauncherIcons.createShortcutIcon(compat, mLauncher, false /* badged */); + + // Create a preview same as the workspace cell size and draw the icon at the + // appropriate position. + int[] size = mLauncher.getWorkspace().estimateItemSize(item, true, false); + preview = Bitmap.createBitmap(size[0], size[1], Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(preview); + DeviceProfile dp = mLauncher.getDeviceProfile(); + c.drawBitmap(icon, (size[0] - icon.getWidth()) / 2, + (size[1] - icon.getHeight() - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2, + new Paint(Paint.FILTER_BITMAP_FLAG)); + } else { + PendingAddWidgetInfo info = new PendingAddWidgetInfo( + LauncherAppWidgetProviderInfo.fromProviderInfo( + mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher))); + int[] size = mLauncher.getWorkspace().estimateItemSize(info, true, false); + + float minScale = 1.25f; + int maxWidth = Math.min((int) (mPreviewRect.width() * minScale), size[0]); + int[] previewSizeBeforeScale = new int[1]; + preview = LauncherAppState.getInstance(mLauncher).getWidgetCache() + .generateWidgetPreview(mLauncher, info.info, maxWidth, null, + previewSizeBeforeScale); + + dragShift.offset( + (mPreviewRect.width() - preview.getWidth()) / 2, + (mPreviewRect.height() - preview.getHeight()) / 2); + item = info; + } + + PendingItemPreviewProvider previewProvider = + new PendingItemPreviewProvider(new View(mLauncher), item, preview); + + // 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(previewProvider); + + Point downPos = new Point((int) event.getX(), (int) event.getY()); + DragOptions options = new DragOptions(); + options.systemDndStartPoint = downPos; + + int x = downPos.x + dragShift.x; + int y = downPos.y + dragShift.y; + mDragController.startDrag( + preview, x, y, this, item, null, null, 1f, options); + mDragStartTime = SystemClock.uptimeMillis(); + return true; + } + + @Override + public boolean supportsAppInfoDropTarget() { + return false; + } + + @Override + public boolean supportsDeleteDropTarget() { + return false; + } + + @Override + public float getIntrinsicIconScaleFactor() { + return 1f; + } + + @Override + public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, + boolean success) { + if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && + !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { + // Exit spring loaded mode if we have not successfully dropped or have not handled the + // drop in Workspace + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + postCleanup(); + } + + @Override + public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target, + LauncherLogProto.Target targetParent) { + // TODO: We should probably log something + } + + private void postCleanup() { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + removeListener(); + } + }); + } + + public void removeListener() { + if (mLauncher != null) { + mLauncher.getDragLayer().setOnDragListener(null); + } + } + + public static final Parcelable.Creator<PinItemDragListener> CREATOR = + new Parcelable.Creator<PinItemDragListener>() { + public PinItemDragListener createFromParcel(Parcel source) { + return new PinItemDragListener(source); + } + + public PinItemDragListener[] newArray(int size) { + return new PinItemDragListener[size]; + } + }; +} diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index d1f878a17..2121b438a 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -26,7 +26,9 @@ import android.graphics.drawable.Drawable; import android.os.Build; import com.android.launcher3.IconCache; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.compat.PinItemRequestCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; /** @@ -40,12 +42,15 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo { // actual existing class. private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut"; + private final PinItemRequestCompat mRequest; private final ShortcutInfo mInfo; private final Context mContext; - public PinShortcutRequestActivityInfo(ShortcutInfo info, Context context) { - super(new ComponentName(info.getPackage(), DUMMY_COMPONENT_CLASS), info.getUserHandle()); - mInfo = info; + public PinShortcutRequestActivityInfo(PinItemRequestCompat request, Context context) { + super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS), + request.getShortcutInfo().getUserHandle()); + mRequest = request; + mInfo = request.getShortcutInfo(); mContext = context; } @@ -61,8 +66,9 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo { } @Override - public boolean startConfigActivity(Activity activity, int requestCode) { - throw new RuntimeException("Not supported"); + public boolean startConfigActivity(Launcher activity, int requestCode) { + activity.onActivityResult(requestCode, Activity.RESULT_OK, mRequest.toIntent()); + return true; } @Override diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 87247f417..455ec4ef8 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -144,12 +144,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } } - public int[] getPreviewSize() { - int[] maxSize = new int[2]; - - maxSize[0] = mPresetPreviewSize; - maxSize[1] = mPresetPreviewSize; - return maxSize; + public WidgetImageView getWidgetView() { + return mWidgetImage; } public void applyPreview(Bitmap bitmap) { @@ -166,12 +162,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { if (mActiveRequest != null) { return; } - int[] size = getPreviewSize(); - if (DEBUG) { - Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):", - getTagToString(), size[0], size[1])); - } - mActiveRequest = mWidgetPreviewLoader.getPreview(mItem, size[0], size[1], this); + mActiveRequest = mWidgetPreviewLoader.getPreview( + mItem, mPresetPreviewSize, mPresetPreviewSize, this); } @Override |