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
This commit is contained in:
parent
658058b960
commit
b38fab7573
|
@ -81,6 +81,8 @@
|
|||
|
||||
<activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
|
||||
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
|
||||
android:excludeFromRecents="true"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:label="@string/action_add_to_workspace" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.pm.action.CONFIRM_PIN_ITEM" />
|
||||
|
|
|
@ -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) {}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue