diff options
| -rw-r--r-- | api/current.xml | 13 | ||||
| -rw-r--r-- | core/java/android/view/DragEvent.java | 23 | ||||
| -rw-r--r-- | core/java/android/view/IWindowSession.aidl | 7 | ||||
| -rw-r--r-- | core/java/android/view/ViewRoot.java | 12 | ||||
| -rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 148 | ||||
| -rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java | 5 |
6 files changed, 158 insertions, 50 deletions
diff --git a/api/current.xml b/api/current.xml index 1055707a9984..22a105ad1208 100644 --- a/api/current.xml +++ b/api/current.xml @@ -190770,6 +190770,17 @@ visibility="public" > </method> +<method name="getResult" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getX" return="float" abstract="false" @@ -190812,6 +190823,8 @@ </parameter> <parameter name="data" type="android.content.ClipData"> </parameter> +<parameter name="result" type="boolean"> +</parameter> </method> <method name="obtain" return="android.view.DragEvent" diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index ebf850524b35..bbac14cc5c3e 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -29,6 +29,7 @@ public class DragEvent implements Parcelable { float mX, mY; ClipDescription mClipDescription; ClipData mClipData; + boolean mDragResult; private DragEvent mNext; private RuntimeException mRecycledLocation; @@ -53,25 +54,27 @@ public class DragEvent implements Parcelable { private DragEvent() { } - private void init(int action, float x, float y, ClipDescription description, ClipData data) { + private void init(int action, float x, float y, ClipDescription description, ClipData data, + boolean result) { mAction = action; mX = x; mY = y; mClipDescription = description; mClipData = data; + mDragResult = result; } static DragEvent obtain() { - return DragEvent.obtain(0, 0f, 0f, null, null); + return DragEvent.obtain(0, 0f, 0f, null, null, false); } public static DragEvent obtain(int action, float x, float y, - ClipDescription description, ClipData data) { + ClipDescription description, ClipData data, boolean result) { final DragEvent ev; synchronized (gRecyclerLock) { if (gRecyclerTop == null) { ev = new DragEvent(); - ev.init(action, x, y, description, data); + ev.init(action, x, y, description, data, result); return ev; } ev = gRecyclerTop; @@ -82,14 +85,14 @@ public class DragEvent implements Parcelable { ev.mRecycled = false; ev.mNext = null; - ev.init(action, x, y, description, data); + ev.init(action, x, y, description, data, result); return ev; } public static DragEvent obtain(DragEvent source) { return obtain(source.mAction, source.mX, source.mY, - source.mClipDescription, source.mClipData); + source.mClipDescription, source.mClipData, source.mDragResult); } public int getAction() { @@ -112,6 +115,10 @@ public class DragEvent implements Parcelable { return mClipDescription; } + public boolean getResult() { + return mDragResult; + } + /** * Recycle the DragEvent, to be re-used by a later caller. After calling * this function you must never touch the event again. @@ -146,7 +153,7 @@ public class DragEvent implements Parcelable { public String toString() { return "DragEvent{" + Integer.toHexString(System.identityHashCode(this)) + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription - + " data=" + mClipData + + " data=" + mClipData + " result=" + mDragResult + "}"; } @@ -160,6 +167,7 @@ public class DragEvent implements Parcelable { dest.writeInt(mAction); dest.writeFloat(mX); dest.writeFloat(mY); + dest.writeInt(mDragResult ? 1 : 0); if (mClipData == null) { dest.writeInt(0); } else { @@ -181,6 +189,7 @@ public class DragEvent implements Parcelable { event.mAction = in.readInt(); event.mX = in.readFloat(); event.mY = in.readFloat(); + event.mDragResult = (in.readInt() != 0); if (in.readInt() != 0) { event.mClipData = ClipData.CREATOR.createFromParcel(in); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 79ea5b68b572..23fae426c5a0 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -130,6 +130,13 @@ interface IWindowSession { boolean performDrag(IWindow window, IBinder dragToken, float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); + /** + * Report the result of a drop action targeted to the given window. + * consumed is 'true' when the drop was accepted by a valid recipient, + * 'false' otherwise. + */ + void reportDropResult(IWindow window, boolean consumed); + /** * Tell the OS that we've just dragged into a View that is willing to accept the drop */ diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index ae671b8bd5ec..ea688adcb91e 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -2506,7 +2506,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn final View prevDragView = mCurrentDragView; // Now dispatch the drag/drop event - mView.dispatchDragEvent(event); + boolean result = mView.dispatchDragEvent(event); // If we changed apparent drag target, tell the OS about it if (prevDragView != mCurrentDragView) { @@ -2521,6 +2521,16 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn Slog.e(TAG, "Unable to note drag target change"); } } + + // Report the drop result if necessary + if (what == DragEvent.ACTION_DROP) { + try { + Log.i(TAG, "Reporting drop result: " + result); + sWindowSession.reportDropResult(mWindow, result); + } catch (RemoteException e) { + Log.e(TAG, "Unable to report drop result"); + } + } } } event.recycle(); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index af2fece2a795..60bd19b09845 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -501,6 +501,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mLocalOnly; ClipData mData; ClipDescription mDataDescription; + boolean mDragResult; float mCurrentX, mCurrentY; float mThumbOffsetX, mThumbOffsetY; InputChannel mServerChannel, mClientChannel; @@ -594,7 +595,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mDragInProgress && newWin.isPotentialDragTarget()) { DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, - desc, null); + desc, null, false); try { newWin.mClient.dispatchDragEvent(event); // track each window that we've notified that the drag is starting @@ -629,25 +630,36 @@ public class WindowManagerService extends IWindowManager.Stub } } - void broadcastDragEnded() { + void broadcastDragEndedLw() { if (DEBUG_DRAG) { Slog.d(TAG, "broadcasting DRAG_ENDED"); } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, 0, 0, null, null); - synchronized (mWindowMap) { - for (WindowState ws: mNotifiedWindows) { - try { - ws.mClient.dispatchDragEvent(evt); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to drag-end window " + ws); - } + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, + 0, 0, null, null, mDragResult); + for (WindowState ws: mNotifiedWindows) { + try { + ws.mClient.dispatchDragEvent(evt); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to drag-end window " + ws); } - mNotifiedWindows.clear(); - mDragInProgress = false; } + mNotifiedWindows.clear(); + mDragInProgress = false; evt.recycle(); } + void endDragLw() { + mDragState.broadcastDragEndedLw(); + + // stop intercepting input + mDragState.unregister(); + mInputMonitor.updateInputWindowsLw(); + + // free our resources and drop all the object references + mDragState.reset(); + mDragState = null; + } + void notifyMoveLw(float x, float y) { final int myPid = Process.myPid(); @@ -667,7 +679,7 @@ public class WindowManagerService extends IWindowManager.Stub // force DRAG_EXITED_EVENT if appropriate DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, - null, null); + null, null, false); mTargetWindow.mClient.dispatchDragEvent(evt); if (myPid != mTargetWindow.mSession.mPid) { evt.recycle(); @@ -679,7 +691,7 @@ public class WindowManagerService extends IWindowManager.Stub } DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null); + null, null, false); touchedWin.mClient.dispatchDragEvent(evt); if (myPid != touchedWin.mSession.mPid) { evt.recycle(); @@ -691,26 +703,43 @@ public class WindowManagerService extends IWindowManager.Stub mTargetWindow = touchedWin; } - // Tell the drop target about the data, and then broadcast the drag-ended notification - void notifyDropLw(float x, float y) { + // Tell the drop target about the data. Returns 'true' if we can immediately + // dispatch the global drag-ended message, 'false' if we need to wait for a + // result from the recipient. + boolean notifyDropLw(float x, float y) { WindowState touchedWin = getTouchedWinAtPointLw(x, y); - if (touchedWin != null) { - if (DEBUG_DRAG) { - Slog.d(TAG, "sending DROP to " + touchedWin); - } - final int myPid = Process.myPid(); - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, - x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, mData); - try { - touchedWin.mClient.dispatchDragEvent(evt); - } catch (RemoteException e) { - Slog.w(TAG, "can't send drop notification to win " + touchedWin); - } + if (touchedWin == null) { + // "drop" outside a valid window -- no recipient to apply a + // timeout to, and we can send the drag-ended message immediately. + mDragResult = false; + return true; + } + + if (DEBUG_DRAG) { + Slog.d(TAG, "sending DROP to " + touchedWin); + } + final int myPid = Process.myPid(); + final IBinder token = touchedWin.mClient.asBinder(); + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, + x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, + null, mData, false); + try { + touchedWin.mClient.dispatchDragEvent(evt); + + // 5 second timeout for this window to respond to the drop + mH.removeMessages(H.DRAG_END_TIMEOUT, token); + Message msg = mH.obtainMessage(H.DRAG_END_TIMEOUT, token); + mH.sendMessageDelayed(msg, 5000); + } catch (RemoteException e) { + Slog.w(TAG, "can't send drop notification to win " + touchedWin); + return true; + } finally { if (myPid != touchedWin.mSession.mPid) { evt.recycle(); } } + mToken = token; + return false; } // Find the visible, touch-deliverable window under the given point @@ -794,9 +823,8 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at " + newX + "," + newY); synchronized (mWindowMap) { - mDragState.notifyDropLw(newX, newY); + endDrag = mDragState.notifyDropLw(newX, newY); } - endDrag = true; } break; case MotionEvent.ACTION_CANCEL: { @@ -808,17 +836,9 @@ public class WindowManagerService extends IWindowManager.Stub if (endDrag) { if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state"); // tell all the windows that the drag has ended - mDragState.broadcastDragEnded(); - - // stop intercepting input - mDragState.unregister(); synchronized (mWindowMap) { - mInputMonitor.updateInputWindowsLw(); + mDragState.endDragLw(); } - - // free our resources and drop all the object references - mDragState.reset(); - mDragState = null; } } } catch (Exception e) { @@ -6251,15 +6271,43 @@ public class WindowManagerService extends IWindowManager.Stub return true; // success! } + public void reportDropResult(IWindow window, boolean consumed) { + IBinder token = window.asBinder(); + if (DEBUG_DRAG) { + Slog.d(TAG, "Drop result=" + consumed + " reported by " + token); + } + + synchronized (mWindowMap) { + if (mDragState.mToken != token) { + Slog.w(TAG, "Invalid drop-result claim by " + window); + throw new IllegalStateException("reportDropResult() by non-recipient"); + } + + // The right window has responded, even if it's no longer around, + // so be sure to halt the timeout even if the later WindowState + // lookup fails. + mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); + + WindowState callingWin = windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(TAG, "Bad result-reporting window " + window); + return; // !!! TODO: throw here? + } + + mDragState.mDragResult = consumed; + mDragState.endDragLw(); + } + } + public void dragRecipientEntered(IWindow window) { if (DEBUG_DRAG) { - Slog.d(TAG, "Drag into new candidate view @ " + window); + Slog.d(TAG, "Drag into new candidate view @ " + window.asBinder()); } } public void dragRecipientExited(IWindow window) { if (DEBUG_DRAG) { - Slog.d(TAG, "Drag from old candidate view @ " + window); + Slog.d(TAG, "Drag from old candidate view @ " + window.asBinder()); } } @@ -8321,6 +8369,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int SEND_NEW_CONFIGURATION = 18; public static final int REPORT_WINDOWS_CHANGE = 19; public static final int DRAG_START_TIMEOUT = 20; + public static final int DRAG_END_TIMEOUT = 21; private Session mLastReportedHold; @@ -8671,12 +8720,27 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { // !!! TODO: ANR the app that has failed to start the drag in time if (mDragState != null) { + mDragState.unregister(); + mInputMonitor.updateInputWindowsLw(); mDragState.reset(); mDragState = null; } } + break; } + case DRAG_END_TIMEOUT: { + IBinder win = (IBinder)msg.obj; + if (DEBUG_DRAG) { + Slog.w(TAG, "Timeout ending drag to win " + win); + } + synchronized (mWindowMap) { + // !!! TODO: ANR the drag-receiving app + mDragState.mDragResult = false; + mDragState.endDragLw(); + } + break; + } } } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 0553f5edd92d..eb0eba2b54fd 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -1093,6 +1093,11 @@ public final class Bridge implements ILayoutBridge { } @SuppressWarnings("unused") + public void reportDropResult(IWindow window, boolean consumed) throws RemoteException { + // pass for now + } + + @SuppressWarnings("unused") public void dragRecipientEntered(IWindow window) throws RemoteException { // pass for now } |