OSDN Git Service

Refactor find and select dialogs
authorCary Clark <cary@android.com>
Fri, 4 Jun 2010 20:37:43 +0000 (16:37 -0400)
committerCary Clark <cary@android.com>
Fri, 25 Jun 2010 18:08:56 +0000 (14:08 -0400)
In WebView:
- Remove the select mode, so that while text is selected,
the view may respond to scroll or zoom gestures.
- Remove the getFindIsUp() method -- let the host application
track if the dialog is visible.
- Map long press to select text if the host application does
not already intercept it.
- Draw find 'extras' during zoom animation.
- Draw select 'extras' during animation and scrolling as well.
- Distinguish between selecting text and pressing a shift key
(the shift key is still a possible select text shortcut)
- Add more public (but hidden) interfaces: setUpSelect, selectAll, etc.

In WebChromeClient, add interfaces to allow WebView to tell the host
application that selection has started or finished. A long press on
a part of the view that does not trigger a host context menu may
start the selection (if text is underneath the press), and clicking
the trackball or tapping away from the current selection may
end the selection.

Companion changes in external/webkit and packages/apps/Browser

Change-Id: I81f549181249c38d8d4c10fd7064f513d11318fa
http://b/262451

core/java/android/webkit/WebChromeClient.java
core/java/android/webkit/WebView.java

index ad53508..ed06a3a 100644 (file)
@@ -322,4 +322,19 @@ public class WebChromeClient {
     public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
         uploadFile.onReceiveValue(null);
     }
+
+    /**
+     * Tell the client that the selection has been initiated.
+     * @hide
+     */
+    public void onSelectionStart() {
+    }
+
+    /**
+     * Tell the client that the selection has been copied or canceled.
+     * @hide
+     */
+    public void onSelectionDone() {
+    }
+
 }
index 96cddb5..842f13a 100644 (file)
@@ -415,8 +415,7 @@ public class WebView extends AbsoluteLayout
     private static final int TOUCH_SHORTPRESS_MODE = 5;
     private static final int TOUCH_DOUBLE_TAP_MODE = 6;
     private static final int TOUCH_DONE_MODE = 7;
-    private static final int TOUCH_SELECT_MODE = 8;
-    private static final int TOUCH_PINCH_DRAG = 9;
+    private static final int TOUCH_PINCH_DRAG = 8;
 
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
@@ -2504,11 +2503,6 @@ public class WebView extends AbsoluteLayout
         return nativeFindIndex();
     }
 
-    /**
-     * @hide
-     */
-    public boolean getFindIsUp() { return mFindIsUp; }
-
     // Used to know whether the find dialog is open.  Affects whether
     // or not we draw the highlights for matches.
     private boolean mFindIsUp;
@@ -3185,9 +3179,28 @@ public class WebView extends AbsoluteLayout
         }
         if (inEditingMode()) {
             return mWebTextView.performLongClick();
-        } else {
-            return super.performLongClick();
         }
+        /* if long click brings up a context menu, the super function
+         * returns true and we're done. Otherwise, nothing happened when
+         * the user clicked. */
+        if (super.performLongClick()) {
+            return true;
+        }
+        /* In the case where the application hasn't already handled the long
+         * click action, look for a word under the  click. If one is found,
+         * animate the text selection into view.
+         * FIXME: no animation code yet */
+        if (mSelectingText) return false; // long click does nothing on selection
+        int x = viewToContentX((int) mLastTouchX + mScrollX);
+        int y = viewToContentY((int) mLastTouchY + mScrollY);
+        setUpSelect();
+        if (mNativeClass != 0 && nativeWordSelection(x, y)) {
+            nativeSetExtendSelection();
+            getWebChromeClient().onSelectionStart();
+            return true;
+        }
+        notifySelectDialogDismissed();
+        return false;
     }
 
     private boolean didUpdateTextViewBounds(boolean allowIntersect) {
@@ -3300,20 +3313,20 @@ public class WebView extends AbsoluteLayout
         if (mNativeClass == 0) return;
         // decide which adornments to draw
         int extras = DRAW_EXTRAS_NONE;
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
+                    + " mSelectingText=" + mSelectingText
+                    + " nativePageShouldHandleShiftAndArrows()="
+                    + nativePageShouldHandleShiftAndArrows()
+                    + " animateZoom=" + animateZoom);
+        }
         if (mFindIsUp) {
-            // When the FindDialog is up, only draw the matches if we are not in
-            // the process of scrolling them into view.
-            if (!animateScroll) {
-                extras = DRAW_EXTRAS_FIND;
-            }
-        } else if (mShiftIsPressed && !nativePageShouldHandleShiftAndArrows()) {
-            if (!mZoomManager.isZoomAnimating()) {
-                extras = DRAW_EXTRAS_SELECTION;
-                nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
-                nativeSetSelectionPointer(!mTouchSelection, mZoomManager.getInvScale(),
-                        mSelectX, mSelectY - getTitleHeight(),
-                        mExtendSelection);
-            }
+            extras = DRAW_EXTRAS_FIND;
+        } else if (mSelectingText) {
+            extras = DRAW_EXTRAS_SELECTION;
+            nativeSetSelectionPointer(mDrawSelectionPointer,
+                    mZoomManager.getInvScale(),
+                    mSelectX, mSelectY - getTitleHeight());
         } else if (drawCursorRing) {
             extras = DRAW_EXTRAS_CURSOR_RING;
         }
@@ -3322,11 +3335,6 @@ public class WebView extends AbsoluteLayout
         if (extras == DRAW_EXTRAS_CURSOR_RING) {
             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                 mTouchMode = TOUCH_SHORTPRESS_MODE;
-                HitTestResult hitTest = getHitTestResult();
-                if (hitTest == null
-                        || hitTest.mType == HitTestResult.UNKNOWN_TYPE) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                }
             }
         }
         if (mFocusSizeChanged) {
@@ -3670,8 +3678,8 @@ public class WebView extends AbsoluteLayout
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (nativePageShouldHandleShiftAndArrows()) {
                 mShiftIsPressed = true;
-            } else if (!nativeCursorWantsKeyEvents() && !mShiftIsPressed) {
-                setUpSelectXY();
+            } else if (!nativeCursorWantsKeyEvents() && !mSelectingText) {
+                setUpSelect();
             }
         }
 
@@ -3692,7 +3700,7 @@ public class WebView extends AbsoluteLayout
                 letPageHandleNavKey(keyCode, event.getEventTime(), true);
                 return true;
             }
-            if (mShiftIsPressed) {
+            if (mSelectingText) {
                 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
                     ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
                 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
@@ -3712,8 +3720,7 @@ public class WebView extends AbsoluteLayout
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             switchOutDrawHistory();
             if (event.getRepeatCount() == 0) {
-                if (mShiftIsPressed
-                        && !nativePageShouldHandleShiftAndArrows()) {
+                if (mSelectingText) {
                     return true; // discard press if copy in progress
                 }
                 mGotCenterDown = true;
@@ -3731,10 +3738,8 @@ public class WebView extends AbsoluteLayout
         if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
                 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
             // turn off copy select if a shift-key combo is pressed
-            mExtendSelection = mShiftIsPressed = false;
-            if (mTouchMode == TOUCH_SELECT_MODE) {
-                mTouchMode = TOUCH_INIT_MODE;
-            }
+            selectionDone();
+            mShiftIsPressed = false;
         }
 
         if (getSettings().getNavDump()) {
@@ -3827,7 +3832,8 @@ public class WebView extends AbsoluteLayout
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (nativePageShouldHandleShiftAndArrows()) {
                 mShiftIsPressed = false;
-            } else if (commitCopy()) {
+            } else if (copySelection()) {
+                selectionDone();
                 return true;
             }
         }
@@ -3848,11 +3854,13 @@ public class WebView extends AbsoluteLayout
             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
             mGotCenterDown = false;
 
-            if (mShiftIsPressed && !nativePageShouldHandleShiftAndArrows()) {
+            if (mSelectingText) {
                 if (mExtendSelection) {
-                    commitCopy();
+                    copySelection();
+                    selectionDone();
                 } else {
                     mExtendSelection = true;
+                    nativeSetExtendSelection();
                     invalidate(); // draw the i-beam instead of the arrow
                 }
                 return true; // discard press if copy in progress
@@ -3897,9 +3905,18 @@ public class WebView extends AbsoluteLayout
         return false;
     }
 
-    private void setUpSelectXY() {
+    /**
+     * @hide pending API council approval.
+     */
+    public void setUpSelect() {
+        if (0 == mNativeClass) return; // client isn't initialized
+        if (inFullScreenMode()) return;
+        if (mSelectingText) return;
         mExtendSelection = false;
-        mShiftIsPressed = true;
+        mSelectingText = mDrawSelectionPointer = true;
+        // don't let the picture change during text selection
+        WebViewCore.pauseUpdatePicture(mWebViewCore);
+        nativeResetSelection();
         if (nativeHasCursorNode()) {
             Rect rect = nativeCursorNodeBounds();
             mSelectX = contentToViewX(rect.left);
@@ -3919,40 +3936,82 @@ public class WebView extends AbsoluteLayout
      * Do not rely on this functionality; it will be deprecated in the future.
      */
     public void emulateShiftHeld() {
+        setUpSelect();
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public void selectAll() {
         if (0 == mNativeClass) return; // client isn't initialized
-        setUpSelectXY();
+        if (inFullScreenMode()) return;
+        if (!mSelectingText) setUpSelect();
+        nativeSelectAll();
+        mDrawSelectionPointer = false;
+        mExtendSelection = true;
+        invalidate();
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public boolean selectDialogIsUp() {
+        return mSelectingText;
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public void notifySelectDialogDismissed() {
+        mSelectingText = false;
+        WebViewCore.resumeUpdatePicture(mWebViewCore);
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public void selectionDone() {
+        if (mSelectingText) {
+            getWebChromeClient().onSelectionDone();
+            invalidate(); // redraw without selection
+            notifySelectDialogDismissed();
+        }
     }
 
-    private boolean commitCopy() {
+    /**
+     * @hide pending API council approval.
+     */
+    public boolean copySelection() {
         boolean copiedSomething = false;
-        if (mExtendSelection) {
-            String selection = nativeGetSelection();
-            if (selection != "") {
-                if (DebugFlags.WEB_VIEW) {
-                    Log.v(LOGTAG, "commitCopy \"" + selection + "\"");
-                }
-                Toast.makeText(mContext
-                        , com.android.internal.R.string.text_copied
-                        , Toast.LENGTH_SHORT).show();
-                copiedSomething = true;
-                try {
-                    IClipboard clip = IClipboard.Stub.asInterface(
-                            ServiceManager.getService("clipboard"));
-                            clip.setClipboardText(selection);
-                } catch (android.os.RemoteException e) {
-                    Log.e(LOGTAG, "Clipboard failed", e);
-                }
+        String selection = getSelection();
+        if (selection != "") {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "copySelection \"" + selection + "\"");
+            }
+            Toast.makeText(mContext
+                    , com.android.internal.R.string.text_copied
+                    , Toast.LENGTH_SHORT).show();
+            copiedSomething = true;
+            try {
+                IClipboard clip = IClipboard.Stub.asInterface(
+                        ServiceManager.getService("clipboard"));
+                        clip.setClipboardText(selection);
+            } catch (android.os.RemoteException e) {
+                Log.e(LOGTAG, "Clipboard failed", e);
             }
-            mExtendSelection = false;
         }
-        mShiftIsPressed = false;
         invalidate(); // remove selection region and pointer
-        if (mTouchMode == TOUCH_SELECT_MODE) {
-            mTouchMode = TOUCH_INIT_MODE;
-        }
         return copiedSomething;
     }
 
+    /**
+     * @hide pending API council approval.
+     */
+    public String getSelection() {
+        if (mNativeClass == 0) return "";
+        return nativeGetSelection();
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -4404,7 +4463,7 @@ public class WebView extends AbsoluteLayout
 
     private boolean shouldForwardTouchEvent() {
         return mFullScreenHolder != null || (mForwardTouchEvents
-                && mTouchMode != TOUCH_SELECT_MODE
+                && !mSelectingText
                 && mPreventDefault != PREVENT_DEFAULT_IGNORE);
     }
 
@@ -4512,16 +4571,6 @@ public class WebView extends AbsoluteLayout
                     mTouchMode = TOUCH_DRAG_START_MODE;
                     mConfirmMove = true;
                     mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
-                } else if (!inFullScreenMode() && mShiftIsPressed) {
-                    mSelectX = mScrollX + (int) x;
-                    mSelectY = mScrollY + (int) y;
-                    mTouchMode = TOUCH_SELECT_MODE;
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
-                    }
-                    nativeMoveSelection(contentX, contentY, false);
-                    mTouchSelection = mExtendSelection = true;
-                    invalidate(); // draw the i-beam instead of the arrow
                 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
                     mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
                     if (getSettings().supportTouchOnly()) {
@@ -4569,6 +4618,14 @@ public class WebView extends AbsoluteLayout
                         EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
                                 (eventTime - mLastTouchUpTime), eventTime);
                     }
+                    if (mSelectingText) {
+                        mDrawSelectionPointer = false;
+                        mSelectionStarted = nativeStartSelection(contentX, contentY);
+                        if (DebugFlags.WEB_VIEW) {
+                            Log.v(LOGTAG, "select=" + contentX + "," + contentY);
+                        }
+                        invalidate();
+                    }
                 }
                 // Trigger the link
                 if (mTouchMode == TOUCH_INIT_MODE
@@ -4658,17 +4715,15 @@ public class WebView extends AbsoluteLayout
                             + " mTouchMode = " + mTouchMode);
                 }
                 mVelocityTracker.addMovement(ev);
-                if (mTouchMode != TOUCH_DRAG_MODE) {
-                    if (mTouchMode == TOUCH_SELECT_MODE) {
-                        mSelectX = mScrollX + (int) x;
-                        mSelectY = mScrollY + (int) y;
-                        if (DebugFlags.WEB_VIEW) {
-                            Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
-                        }
-                        nativeMoveSelection(contentX, contentY, true);
-                        invalidate();
-                        break;
+                if (mSelectingText && mSelectionStarted) {
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
                     }
+                    nativeExtendSelection(contentX, contentY);
+                    invalidate();
+                    break;
+                }
+                if (mTouchMode != TOUCH_DRAG_MODE) {
 
                     if (!mConfirmMove) {
                         break;
@@ -4830,10 +4885,6 @@ public class WebView extends AbsoluteLayout
                             mTouchMode = TOUCH_DONE_MODE;
                         }
                         break;
-                    case TOUCH_SELECT_MODE:
-                        commitCopy();
-                        mTouchSelection = false;
-                        break;
                     case TOUCH_INIT_MODE: // tap
                     case TOUCH_SHORTPRESS_START_MODE:
                     case TOUCH_SHORTPRESS_MODE:
@@ -4872,6 +4923,13 @@ public class WebView extends AbsoluteLayout
                                 break;
                             }
                         } else {
+                            if (mSelectingText) {
+                                // tapping on selection or controls does nothing
+                                if (!nativeHitSelection(contentX, contentY)) {
+                                    selectionDone();
+                                }
+                                break;
+                            }
                             // only trigger double tap if the WebView is
                             // scalable
                             if (mTouchMode == TOUCH_INIT_MODE
@@ -5026,8 +5084,10 @@ public class WebView extends AbsoluteLayout
     private float mTrackballRemainsY = 0.0f;
     private int mTrackballXMove = 0;
     private int mTrackballYMove = 0;
+    private boolean mSelectingText = false;
+    private boolean mSelectionStarted = false;
     private boolean mExtendSelection = false;
-    private boolean mTouchSelection = false;
+    private boolean mDrawSelectionPointer = false;
     private static final int TRACKBALL_KEY_TIMEOUT = 1000;
     private static final int TRACKBALL_TIMEOUT = 200;
     private static final int TRACKBALL_WAIT = 100;
@@ -5066,10 +5126,8 @@ public class WebView extends AbsoluteLayout
             if (ev.getY() < 0) pageUp(true);
             return true;
         }
-        boolean shiftPressed = mShiftIsPressed && (mNativeClass == 0
-                || !nativePageShouldHandleShiftAndArrows());
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (shiftPressed) {
+            if (mSelectingText) {
                 return true; // discard press if copy in progress
             }
             mTrackballDown = true;
@@ -5094,11 +5152,13 @@ public class WebView extends AbsoluteLayout
             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
             mTrackballDown = false;
             mTrackballUpTime = time;
-            if (shiftPressed) {
+            if (mSelectingText) {
                 if (mExtendSelection) {
-                    commitCopy();
+                    copySelection();
+                    selectionDone();
                 } else {
                     mExtendSelection = true;
+                    nativeSetExtendSelection();
                     invalidate(); // draw the i-beam instead of the arrow
                 }
                 return true; // discard press if copy in progress
@@ -5165,8 +5225,7 @@ public class WebView extends AbsoluteLayout
                     + " yRate=" + yRate
                     );
         }
-        nativeMoveSelection(viewToContentX(mSelectX),
-                viewToContentY(mSelectY), mExtendSelection);
+        nativeMoveSelection(viewToContentX(mSelectX), viewToContentY(mSelectY));
         int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
                 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
                 : 0;
@@ -5232,8 +5291,16 @@ public class WebView extends AbsoluteLayout
         float yRate = mTrackballRemainsY * 1000 / elapsed;
         int viewWidth = getViewWidth();
         int viewHeight = getViewHeight();
-        if (mShiftIsPressed && (mNativeClass == 0
-                || !nativePageShouldHandleShiftAndArrows())) {
+        if (mSelectingText) {
+            if (!mDrawSelectionPointer) {
+                // The last selection was made by touch, disabling drawing the
+                // selection pointer. Allow the trackball to adjust the
+                // position of the touch control.
+                mSelectX = contentToViewX(nativeSelectionX());
+                mSelectY = contentToViewY(nativeSelectionY());
+                mDrawSelectionPointer = mExtendSelection = true;
+                nativeSetExtendSelection();
+            }
             moveSelection(scaleTrackballX(xRate, viewWidth),
                     scaleTrackballY(yRate, viewHeight));
             mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -6923,9 +6990,10 @@ public class WebView extends AbsoluteLayout
     private native boolean  nativeCursorWantsKeyEvents();
     private native void     nativeDebugDump();
     private native void     nativeDestroy();
-    private native boolean  nativeEvaluateLayersAnimations();
     private native void     nativeDrawExtras(Canvas canvas, int extra);
     private native void     nativeDumpDisplayTree(String urlOrNull);
+    private native boolean  nativeEvaluateLayersAnimations();
+    private native void     nativeExtendSelection(int x, int y);
     private native int      nativeFindAll(String findLower, String findUpper);
     private native void     nativeFindNext(boolean forward);
     /* package */ native int      nativeFocusCandidateFramePointer();
@@ -6952,6 +7020,7 @@ public class WebView extends AbsoluteLayout
     private native boolean  nativeHasCursorNode();
     private native boolean  nativeHasFocusNode();
     private native void     nativeHideCursor();
+    private native boolean  nativeHitSelection(int x, int y);
     private native String   nativeImageURI(int x, int y);
     private native void     nativeInstrumentReport();
     /* package */ native boolean nativeMoveCursorToNextTextInput();
@@ -6961,8 +7030,7 @@ public class WebView extends AbsoluteLayout
     private native boolean  nativeMoveCursor(int keyCode, int count,
             boolean noScroll);
     private native int      nativeMoveGeneration();
-    private native void     nativeMoveSelection(int x, int y,
-            boolean extendSelection);
+    private native void     nativeMoveSelection(int x, int y);
     /**
      * @return true if the page should get the shift and arrow keys, rather
      * than select text/navigation.
@@ -6976,22 +7044,28 @@ public class WebView extends AbsoluteLayout
     // mNativeClass is not null before calling this method.
     private native void     nativeRecordButtons(boolean focused,
             boolean pressed, boolean invalidate);
+    private native void     nativeResetSelection();
+    private native void     nativeSelectAll();
     private native void     nativeSelectBestAt(Rect rect);
+    private native int      nativeSelectionX();
+    private native int      nativeSelectionY();
     private native int      nativeFindIndex();
+    private native void     nativeSetExtendSelection();
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
     private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
     private native void     nativeSetRootLayer(int layer);
     private native void     nativeSetSelectionPointer(boolean set,
-            float scale, int x, int y, boolean extendSelection);
-    private native void     nativeSetSelectionRegion(boolean set);
+            float scale, int x, int y);
+    private native boolean  nativeStartSelection(int x, int y);
     private native Rect     nativeSubtractLayers(Rect content);
     private native int      nativeTextGeneration();
     // Never call this version except by updateCachedTextfield(String) -
     // we always want to pass in our generation number.
     private native void     nativeUpdateCachedTextfield(String updatedText,
             int generation);
+    private native boolean  nativeWordSelection(int x, int y);
     // return NO_LEFTEDGE means failure.
     static final int NO_LEFTEDGE = -1;
     native int nativeGetBlockLeftEdge(int x, int y, float scale);