Initial changes to drag and drop to docked task.
Change-Id: I5e7a435a4061d902e5de0a54cc386388bc2b565e
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 5c61c5ca6..284d540 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -26,6 +26,7 @@
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.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 @@
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();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 52b9521..59c590c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -22,6 +22,7 @@
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 final int smallestWidth;
/** Misc **/
+ public boolean hasDockedTasks;
public boolean useHardwareLayers;
public boolean fakeShadows;
public int svelteLevel;
@@ -114,6 +116,7 @@
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.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
new file mode 100644
index 0000000..f2c3c33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
@@ -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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
new file mode 100644
index 0000000..827998d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
new file mode 100644
index 0000000..2d42a0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
@@ -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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index e0f820d..119a6f2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -327,6 +327,21 @@
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;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index e810410..93c5ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -21,12 +21,29 @@
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) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 8eec87e..0fb235b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -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.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 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;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
new file mode 100644
index 0000000..96dfaac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
@@ -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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index de8730d..e406155 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -22,6 +22,7 @@
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.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 @@
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
+
+ RecentsViewTouchHandler mTouchHandler;
+ DragView mDragView;
+ ColorDrawable mDockRegionOverlay;
+
Interpolator mFastOutSlowInInterpolator;
Rect mSystemInsets = new Rect();
@@ -96,10 +108,14 @@
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 @@
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 @@
}
}
+ @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 @@
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 @@
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 @@
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 @@
.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();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
new file mode 100644
index 0000000..8dea0cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 67e3f82..bc1f481 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1296,7 +1296,7 @@
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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 910db87..f213a10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -21,13 +21,17 @@
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.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 @@
View mActionButtonView;
TaskViewCallbacks mCb;
+ Point mDownTouchPos = new Point();
+
Interpolator mFastOutSlowInInterpolator;
Interpolator mFastOutLinearInInterpolator;
Interpolator mQuintOutInterpolator;
@@ -169,6 +180,14 @@
}
@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);
int height = MeasureSpec.getSize(heightMeasureSpec);
@@ -719,6 +738,7 @@
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
mActionButtonView.setOnClickListener(this);
+ setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
}
mTaskDataLoaded = true;
}
@@ -753,4 +773,69 @@
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);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 7de8b7b..a71fdaa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -471,7 +471,7 @@
// 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