Initial changes to drag and drop to docked task.

Change-Id: I5e7a435a4061d902e5de0a54cc386388bc2b565e
This commit is contained in:
Winson
2015-10-01 17:24:51 -07:00
parent feb0dfc0e7
commit be7607af88
14 changed files with 627 additions and 3 deletions

View File

@@ -26,6 +26,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
@@ -41,6 +42,8 @@ import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEven
import com.android.systemui.recents.events.ui.DismissTaskEvent;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -612,6 +615,18 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
}
public final void onBusEvent(DragStartEvent event) {
// Lock the orientation while dragging
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
// TODO: docking requires custom accessibility actions
}
public final void onBusEvent(DragEndEvent event) {
// Unlock the orientation when dragging completes
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();

View File

@@ -22,6 +22,7 @@ import android.graphics.Rect;
import android.provider.Settings;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
/**
* Application resources that can be retrieved from the application context and are not specifically
@@ -70,6 +71,7 @@ public class RecentsConfiguration {
public final int smallestWidth;
/** Misc **/
public boolean hasDockedTasks;
public boolean useHardwareLayers;
public boolean fakeShadows;
public int svelteLevel;
@@ -114,6 +116,7 @@ public class RecentsConfiguration {
lockToAppEnabled = ssp.getSystemSetting(context,
Settings.System.LOCK_TO_APP_ENABLED) != 0;
multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
hasDockedTasks = ssp.hasDockedTask();
// Recompute some values based on the given state, since we can not rely on the resource
// system to get certain values.

View File

@@ -0,0 +1,35 @@
/*
* 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.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
/**
* This event is sent when a user drag enters or exits a dock region.
*/
public class DragDockStateChangedEvent extends EventBus.Event {
public final Task task;
public final TaskStack.DockState dockState;
public DragDockStateChangedEvent(Task task, TaskStack.DockState dockState) {
this.task = task;
this.dockState = dockState;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.DragView;
import com.android.systemui.recents.views.TaskView;
/**
* This event is sent whenever a drag ends.
*/
public class DragEndEvent extends EventBus.Event {
public final Task task;
public final TaskView taskView;
public final DragView dragView;
public final TaskStack.DockState dockState;
public final ReferenceCountedTrigger postAnimationTrigger;
public DragEndEvent(Task task, TaskView taskView, DragView dragView,
TaskStack.DockState dockState, ReferenceCountedTrigger postAnimationTrigger) {
this.task = task;
this.taskView = taskView;
this.dragView = dragView;
this.dockState = dockState;
this.postAnimationTrigger = postAnimationTrigger;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.DragView;
import com.android.systemui.recents.views.TaskView;
/**
* This event is sent whenever a drag starts.
*/
public class DragStartEvent extends EventBus.Event {
public final Task task;
public final TaskView taskView;
public final DragView dragView;
public DragStartEvent(Task task, TaskView taskView, DragView dragView) {
this.task = task;
this.taskView = taskView;
this.dragView = dragView;
}
}

View File

@@ -327,6 +327,21 @@ public class SystemServicesProxy {
return mAm.isInHomeStack(taskId);
}
/**
* @return whether there are any docked tasks.
*/
public boolean hasDockedTask() {
if (mIam == null) return false;
ActivityManager.StackInfo stackInfo = null;
try {
stackInfo = mIam.getStackInfo(ActivityManager.DOCKED_STACK_ID);
} catch (RemoteException e) {
e.printStackTrace();
}
return stackInfo != null;
}
/** Returns the top task thumbnail for the given task id */
public Bitmap getTaskThumbnail(int taskId) {
if (mAm == null) return null;

View File

@@ -21,12 +21,29 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewParent;
import java.util.ArrayList;
/* Common code */
public class Utilities {
/**
* @return the first parent walking up the view hierarchy that has the given class type.
*
* @param parentClass must be a class derived from {@link View}
*/
public static <T extends View> T findParent(View v, Class<T> parentClass) {
ViewParent parent = v.getParent();
while (parent != null) {
if (parent.getClass().equals(parentClass)) {
return (T) parent;
}
parent = parent.getParent();
}
return null;
}
/** Scales a rect about its centroid */
public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) {

View File

@@ -16,8 +16,11 @@
package com.android.systemui.recents.model;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.misc.NamedCounter;
@@ -30,6 +33,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Random;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
/**
* An interface for a task filter to query whether a particular task should show in a stack.
@@ -174,6 +180,63 @@ public class TaskStack {
public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
}
public enum DockState {
LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.5f, 1), new RectF(0.5f, 0, 1, 1)),
TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.5f), new RectF(0, 0.5f, 1, 1)),
RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
new RectF(0.75f, 0, 1, 1), new RectF(0.5f, 0, 1, 1), new RectF(0, 0, 0.5f, 1)),
BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
new RectF(0, 0.75f, 1, 1), new RectF(0, 0.5f, 1, 1), new RectF(0, 0, 1, 0.5f));
public final int createMode;
private final RectF touchArea;
private final RectF dockArea;
private final RectF stackArea;
/**
* @param createMode used to pass to ActivityManager to dock the task
* @param touchArea the area in which touch will initiate this dock state
* @param stackArea the area for the stack if a task is docked
*/
DockState(int createMode, RectF touchArea, RectF dockArea, RectF stackArea) {
this.createMode = createMode;
this.touchArea = touchArea;
this.dockArea = dockArea;
this.stackArea = stackArea;
}
/**
* Returns whether {@param x} and {@param y} are contained in the touch area scaled to the
* given {@param width} and {@param height}.
*/
public boolean touchAreaContainsPoint(int width, int height, float x, float y) {
int left = (int) (touchArea.left * width);
int top = (int) (touchArea.top * height);
int right = (int) (touchArea.right * width);
int bottom = (int) (touchArea.bottom * height);
return x >= left && y >= top && x <= right && y <= bottom;
}
/**
* Returns the docked task bounds with the given {@param width} and {@param height}.
*/
public Rect getDockedBounds(int width, int height) {
return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
(int) (dockArea.right * width), (int) (dockArea.bottom * height));
}
/**
* Returns the stack bounds with the given {@param width} and {@param height}.
*/
public Rect getStackBounds(int width, int height) {
return new Rect((int) (stackArea.left * width), (int) (stackArea.top * height),
(int) (stackArea.right * width), (int) (stackArea.bottom * height));
}
}
// The task offset to apply to a task id as a group affiliation
static final int IndividualTaskIdOffset = 1 << 16;

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2014 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.systemui.recents.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.widget.ImageView;
public class DragView extends ImageView {
// The offset from the top-left of the dragBitmap
Point mTopLeftOffset;
public DragView(Context context, Bitmap dragBitmap, Point tlOffset) {
super(context);
mTopLeftOffset = tlOffset;
setImageBitmap(dragBitmap);
}
public Point getTopLeftOffset() {
return mTopLeftOffset;
}
}

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -30,19 +31,25 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.DismissTaskEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -78,6 +85,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
RecentsViewTouchHandler mTouchHandler;
DragView mDragView;
ColorDrawable mDockRegionOverlay;
Interpolator mFastOutSlowInInterpolator;
Rect mSystemInsets = new Rect();
@@ -96,10 +108,14 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mTouchHandler = new RecentsViewTouchHandler(this);
mDockRegionOverlay = new ColorDrawable(0x66000000);
mDockRegionOverlay.setAlpha(0);
}
/** Sets the callbacks */
@@ -200,6 +216,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
ArrayList<Task> tasks = stack.getTasks();
// Find the launch task in the stack
// TODO: replace this with an event from RecentsActivity
if (!tasks.isEmpty()) {
int taskCount = tasks.size();
for (int j = 0; j < taskCount; j++) {
@@ -267,6 +284,20 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 1);
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
EventBus.getDefault().unregister(mTouchHandler);
}
/**
* This is called with the full size of the window since we are handling our own insets.
*/
@@ -293,6 +324,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
if (mDragView != null) {
mDragView.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
setMeasuredDimension(width, height);
}
@@ -314,6 +351,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
if (mDragView != null) {
mDragView.layout(left, top, left + mDragView.getMeasuredWidth(),
top + mDragView.getMeasuredHeight());
}
}
@Override
@@ -323,6 +365,24 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
return insets.consumeSystemWindowInsets();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mTouchHandler.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return mTouchHandler.onTouchEvent(ev);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mDockRegionOverlay.getAlpha() > 0) {
mDockRegionOverlay.draw(canvas);
}
}
/** Notifies each task view of the user interaction. */
public void onUserInteraction() {
// Get the first stack view
@@ -697,4 +757,64 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
.start();
}
}
/**** EventBus Events ****/
public final void onBusEvent(DragStartEvent event) {
// Add the drag view
mDragView = event.dragView;
addView(mDragView);
}
public final void onBusEvent(DragDockStateChangedEvent event) {
// Update the task stack positions, and then
if (event.dockState != null) {
// Draw an overlay on the bounds of the dock task
mDockRegionOverlay.setBounds(
event.dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
mDockRegionOverlay.setAlpha(255);
} else {
mDockRegionOverlay.setAlpha(0);
}
invalidate();
}
public final void onBusEvent(final DragEndEvent event) {
event.postAnimationTrigger.increment();
event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
// Remove the drag view
removeView(mDragView);
mDragView = null;
mDockRegionOverlay.setAlpha(0);
invalidate();
// Dock the new task if we are hovering over a valid dock state
if (event.dockState != null) {
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
ssp.setTaskResizeable(event.task.key.id);
ssp.dockTask(event.task.key.id, event.dockState.createMode);
launchTask(event.task, null);
}
}
});
if (event.dockState == null) {
// Animate the alpha back to what it was before
Rect taskBounds = mTaskStackView.getStackAlgorithm().getUntransformedTaskViewBounds();
int left = taskBounds.left + (int) ((1f - event.taskView.getScaleX()) * taskBounds.width()) / 2;
int top = taskBounds.top + (int) ((1f - event.taskView.getScaleY()) * taskBounds.height()) / 2;
event.dragView.animate()
.alpha(1f)
.translationX(left + event.taskView.getTranslationX())
.translationY(top + event.taskView.getTranslationY())
.setDuration(175)
.setInterpolator(new AccelerateInterpolator(1.5f))
.withEndAction(event.postAnimationTrigger.decrementAsRunnable())
.withLayer()
.start();
} else {
event.postAnimationTrigger.decrement();
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2014 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.systemui.recents.views;
import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
/**
* Represents the dock regions for each orientation.
*/
class DockRegion {
public static TaskStack.DockState[] LANDSCAPE = {
TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
};
public static TaskStack.DockState[] PORTRAIT = {
TaskStack.DockState.TOP, TaskStack.DockState.BOTTOM
};
}
/**
* Handles touch events for a RecentsView.
*/
class RecentsViewTouchHandler {
private RecentsView mRv;
private Task mDragTask;
private TaskView mTaskView;
private DragView mDragView;
private Point mDownPos = new Point();
private boolean mDragging;
private TaskStack.DockState mLastDockState;
public RecentsViewTouchHandler(RecentsView rv) {
mRv = rv;
}
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mDownPos.set((int) ev.getX(), (int) ev.getY());
break;
}
return mDragging;
}
/** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) {
if (!mDragging) return false;
boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mDownPos.set((int) ev.getX(), (int) ev.getY());
break;
case MotionEvent.ACTION_MOVE: {
int width = mRv.getMeasuredWidth();
int height = mRv.getMeasuredHeight();
float evX = ev.getX();
float evY = ev.getY();
float x = evX - mDragView.getTopLeftOffset().x;
float y = evY - mDragView.getTopLeftOffset().y;
// Update the dock state
TaskStack.DockState[] dockStates = isLandscape ?
DockRegion.LANDSCAPE : DockRegion.PORTRAIT;
TaskStack.DockState foundDockState = null;
for (int i = 0; i < dockStates.length; i++) {
TaskStack.DockState state = dockStates[i];
if (state.touchAreaContainsPoint(width, height, evX, evY)) {
foundDockState = state;
break;
}
}
if (mLastDockState != foundDockState) {
mLastDockState = foundDockState;
EventBus.getDefault().send(new DragDockStateChangedEvent(mDragTask,
foundDockState));
}
mDragView.setTranslationX(x);
mDragView.setTranslationY(y);
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
mRv.getContext(), null, null, null);
postAnimationTrigger.increment();
EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView,
mLastDockState, postAnimationTrigger));
postAnimationTrigger.decrement();
break;
}
}
return true;
}
/**** Events ****/
public final void onBusEvent(DragStartEvent event) {
mRv.getParent().requestDisallowInterceptTouchEvent(true);
mDragging = true;
mDragTask = event.task;
mTaskView = event.taskView;
mDragView = event.dragView;
float x = mDownPos.x - mDragView.getTopLeftOffset().x;
float y = mDownPos.y - mDragView.getTopLeftOffset().y;
mDragView.setTranslationX(x);
mDragView.setTranslationY(y);
}
public final void onBusEvent(DragEndEvent event) {
mDragging = false;
mDragTask = null;
mTaskView = null;
mDragView = null;
mLastDockState = null;
}
}

View File

@@ -1296,7 +1296,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
RecentsTaskLoader.getInstance().loadTaskData(task);
// If the doze trigger has already fired, then update the state for this task view
if (mUIDozeTrigger.hasTriggered()) {
if (mUIDozeTrigger.hasTriggered() || mConfig.multiWindowEnabled) {
tv.setNoUserInteractionState();
}

View File

@@ -21,13 +21,17 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateInterpolator;
@@ -36,17 +40,22 @@ import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.DismissTaskEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
/* A task view */
public class TaskView extends FrameLayout implements Task.TaskCallbacks,
View.OnClickListener {
View.OnClickListener, View.OnLongClickListener {
/** The TaskView callbacks */
interface TaskViewCallbacks {
@@ -79,6 +88,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
View mActionButtonView;
TaskViewCallbacks mCb;
Point mDownTouchPos = new Point();
Interpolator mFastOutSlowInInterpolator;
Interpolator mFastOutLinearInInterpolator;
Interpolator mQuintOutInterpolator;
@@ -168,6 +179,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
}
return super.onInterceptTouchEvent(ev);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -719,6 +738,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
mActionButtonView.setOnClickListener(this);
setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
}
mTaskDataLoaded = true;
}
@@ -753,4 +773,69 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mCb.onTaskViewClicked(this, mTask, (v == mActionButtonView));
}
}
/**** View.OnLongClickListener Implementation ****/
@Override
public boolean onLongClick(View v) {
if (v == this) {
// Start listening for drag events
setClipViewInStack(false);
int width = (int) (getScaleX() * getWidth());
int height = (int) (getScaleY() * getHeight());
Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(dragBitmap);
c.scale(getScaleX(), getScaleY());
mThumbnailView.draw(c);
mHeaderView.draw(c);
c.setBitmap(null);
// Initiate the drag
final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos);
dragView.setOutlineProvider(mViewBounds);
dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
// Hide this task view after the drag view is attached
setVisibility(View.INVISIBLE);
// Animate the alpha slightly to indicate dragging
dragView.setElevation(getElevation());
dragView.setTranslationZ(getTranslationZ());
dragView.animate()
.alpha(0.75f)
.setDuration(175)
.setInterpolator(new AccelerateInterpolator(1.5f))
.withLayer()
.start();
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
EventBus.getDefault().send(new DragStartEvent(mTask, this, dragView));
return true;
}
return false;
}
/**** Events ****/
public final void onBusEvent(DragEndEvent event) {
event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
// If docked state == null:
// Animate the drag view back from where it is, to the view location, then after it returns,
// update the clip state
setClipViewInStack(true);
// Show this task view
setVisibility(View.VISIBLE);
}
});
EventBus.getDefault().unregister(this);
}
}

View File

@@ -471,7 +471,7 @@ public class TaskViewHeader extends FrameLayout
// In accessibility, a single click on the focused app info button will show it
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
} else if (v == mDismissButton) {
TaskView tv = (TaskView) getParent().getParent();
TaskView tv = Utilities.findParent(this, TaskView.class);
tv.dismissTask();
// Keep track of deletions by the dismiss button