Bug 1441279 - 1. Add SelectionActionDelegate interface; r?snorp r?esawin draft
authorJim Chen <nchen@mozilla.com>
Mon, 02 Apr 2018 17:13:45 -0400
changeset 776310 853f501fad4bcd57fcef8ce814d89f6c3fca7f34
parent 776018 c44f60c43432d468639b5fe078420e60c13fd3de
child 776311 63d496ee0e4c82e83e0c04f15325a7c0e353698b
push id104840
push userbmo:nchen@mozilla.com
push dateMon, 02 Apr 2018 21:15:36 +0000
reviewerssnorp, esawin
bugs1441279
milestone61.0a1
Bug 1441279 - 1. Add SelectionActionDelegate interface; r?snorp r?esawin Add a SelectionActionDelegate interface for handling text selection. MozReview-Commit-ID: I37Hm6nphJx
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
@@ -5,17 +5,18 @@ package org.mozilla.geckoview.test.util
 
 import org.mozilla.geckoview.GeckoSession
 
 class Callbacks private constructor() {
     object Default : All {
     }
 
     interface All : ContentDelegate, NavigationDelegate, PermissionDelegate, ProgressDelegate,
-                    PromptDelegate, ScrollDelegate, TrackingProtectionDelegate {
+                    PromptDelegate, ScrollDelegate, SelectionActionDelegate,
+                    TrackingProtectionDelegate {
     }
 
     interface ContentDelegate : GeckoSession.ContentDelegate {
         override fun onTitleChange(session: GeckoSession, title: String) {
         }
 
         override fun onFocusRequest(session: GeckoSession) {
         }
@@ -115,9 +116,17 @@ class Callbacks private constructor() {
         override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
         }
     }
 
     interface TrackingProtectionDelegate : GeckoSession.TrackingProtectionDelegate {
         override fun onTrackerBlocked(session: GeckoSession, uri: String, categories: Int) {
         }
     }
+
+    interface SelectionActionDelegate : GeckoSession.SelectionActionDelegate {
+        override fun onShowActionRequest(session: GeckoSession, selection: GeckoSession.SelectionActionDelegate.Selection, actions: Array<out String>, response: GeckoSession.Response<String>) {
+        }
+
+        override fun onHideAction(session: GeckoSession, reason: Int) {
+        }
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -1,16 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.UUID;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.gfx.LayerSession;
@@ -24,27 +26,29 @@ import org.mozilla.gecko.util.BundleEven
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.RectF;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.NonNull;
+import android.support.annotation.StringDef;
 import android.util.Base64;
 import android.util.Log;
 
 public class GeckoSession extends LayerSession
                           implements Parcelable {
     private static final String LOGTAG = "GeckoSession";
     private static final boolean DEBUG = false;
 
@@ -1719,16 +1723,186 @@ public class GeckoSession extends LayerS
          * by Gecko (e.g., a download).
          *
          * @param session the GeckoSession that received the external response.
          * @param response the WebResponseInfo for the external response
          */
         void onExternalResponse(GeckoSession session, WebResponseInfo response);
     }
 
+    public interface SelectionActionDelegate {
+        @IntDef(flag = true, value = {FLAG_IS_COLLAPSED,
+                                      FLAG_IS_EDITABLE})
+        @interface Flag {}
+
+        /**
+         * The selection is collapsed at a single position.
+         */
+        final int FLAG_IS_COLLAPSED = 1;
+        /**
+         * The selection is inside editable content such as an input element or
+         * contentEditable node.
+         */
+        final int FLAG_IS_EDITABLE = 2;
+
+        @StringDef({ACTION_CUT,
+                    ACTION_COPY,
+                    ACTION_DELETE,
+                    ACTION_PASTE,
+                    ACTION_SELECT_ALL,
+                    ACTION_UNSELECT,
+                    ACTION_COLLAPSE_TO_START,
+                    ACTION_COLLAPSE_TO_END})
+        @interface Action {}
+
+        /**
+         * Copy onto the clipboard then delete the selected content. Selection
+         * must be editable.
+         */
+        final String ACTION_CUT = "org.mozilla.geckoview.CUT";
+        /**
+         * Copy the selected content onto the clipboard.
+         */
+        final String ACTION_COPY = "org.mozilla.geckoview.COPY";
+        /**
+         * Delete the selected content. Selection must be editable.
+         */
+        final String ACTION_DELETE = "org.mozilla.geckoview.DELETE";
+        /**
+         * Replace the selected content with the clipboard content. Selection
+         * must be editable.
+         */
+        final String ACTION_PASTE = "org.mozilla.geckoview.PASTE";
+        /**
+         * Select the entire content of the document or editor.
+         */
+        final String ACTION_SELECT_ALL = "org.mozilla.geckoview.SELECT_ALL";
+        /**
+         * Clear the current selection. Selection must not be editable.
+         */
+        final String ACTION_UNSELECT = "org.mozilla.geckoview.UNSELECT";
+        /**
+         * Collapse the current selection to its start position.
+         * Selection must be editable.
+         */
+        final String ACTION_COLLAPSE_TO_START = "org.mozilla.geckoview.COLLAPSE_TO_START";
+        /**
+         * Collapse the current selection to its end position.
+         * Selection must be editable.
+         */
+        final String ACTION_COLLAPSE_TO_END = "org.mozilla.geckoview.COLLAPSE_TO_END";
+
+        /**
+         * Represents attributes of a selection.
+         */
+        class Selection {
+            /**
+             * Flags describing the current selection, as a bitwise combination
+             * of the {@link #FLAG_IS_COLLAPSED FLAG_*} constants.
+             */
+            public final @Flag int flags;
+
+            /**
+             * Text content of the current selection. An empty string indicates the selection
+             * is collapsed or the selection cannot be represented as plain text.
+             */
+            public final String text;
+
+            /**
+             * The bounds of the current selection in client coordinates. Use {@link
+             * GeckoSession#getClientToScreenMatrix} to perform transformation to screen
+             * coordinates.
+             */
+            public final RectF clientRect;
+
+            /* package */ Selection(final GeckoBundle bundle) {
+                flags = (bundle.getBoolean("collapsed") ?
+                         SelectionActionDelegate.FLAG_IS_COLLAPSED : 0) |
+                        (bundle.getBoolean("editable") ?
+                         SelectionActionDelegate.FLAG_IS_EDITABLE : 0);
+                text = bundle.getString("selection");
+
+                final GeckoBundle rectBundle = bundle.getBundle("clientRect");
+                if (rectBundle == null) {
+                    clientRect = null;
+                } else {
+                    clientRect = new RectF((float) rectBundle.getDouble("left"),
+                                           (float) rectBundle.getDouble("top"),
+                                           (float) rectBundle.getDouble("right"),
+                                           (float) rectBundle.getDouble("bottom"));
+                }
+            }
+        }
+
+        /**
+         * Selection actions are available. Selection actions become available when the
+         * user selects some content in the document or editor. Inside an editor,
+         * selection actions can also become available when the user explicitly requests
+         * editor action UI, for example by tapping on the caret handle.
+         *
+         * In response to this callback, applications typically display a toolbar
+         * containing the selection actions. To perform a certain action, pass the Action
+         * object back through the response parameter, which may be used multiple times to
+         * perform multiple actions at once.
+         *
+         * Once a {@link #onHideAction} call (with particular reasons) or another {@link
+         * #onShowActionRequest} call is received, any previously received actions are no
+         * longer unavailable.
+         *
+         * @param session The GeckoSession that initiated the callback.
+         * @param selection Current selection attributes.
+         * @param actions List of built-in actions available.
+         * @param response Callback object for performing built-in actions. For example,
+         * {@code response.respond(actions[0])} performs the first action. May be used
+         * multiple times to perform multiple actions at once.
+         */
+        void onShowActionRequest(GeckoSession session, Selection selection,
+                                 @Action String[] actions, Response<String> response);
+
+        @IntDef({HIDE_REASON_NO_SELECTION,
+                 HIDE_REASON_INVISIBLE_SELECTION,
+                 HIDE_REASON_ACTIVE_SELECTION,
+                 HIDE_REASON_ACTIVE_SCROLL})
+        @interface HideReason {}
+
+        /**
+         * Actions are no longer available due to the user clearing the selection.
+         */
+        final int HIDE_REASON_NO_SELECTION = 0;
+        /**
+         * Actions are no longer available due to the user moving the selection becoming
+         * out of view. Previous actions are still available after a callback with this
+         * reason.
+         */
+        final int HIDE_REASON_INVISIBLE_SELECTION = 1;
+        /**
+         * Actions are no longer available due to the user actively changing the
+         * selection. {@link #onShowActionRequest} may be called again once the user has
+         * set a selection, if the new selection has available actions.
+         */
+        final int HIDE_REASON_ACTIVE_SELECTION = 2;
+        /**
+         * Actions are no longer available due to the user actively scrolling the page.
+         * {@link #onShowActionRequest} may be called again once the user has stopped
+         * scrolling the page, if the selection is still visible. Until then, previous
+         * actions are still available after a callback with this reason.
+         */
+        final int HIDE_REASON_ACTIVE_SCROLL = 3;
+
+        /**
+         * Previous actions are no longer available due to the user interacting with the
+         * page. Applications typically hide the action toolbar in response.
+         *
+         * @param session The GeckoSession that initiated the callback.
+         * @param reason The reason that actions are no longer available, as one of the
+         * {@link #HIDE_REASON_NO_SELECTION HIDE_REASON_*} constants.
+         */
+        void onHideAction(GeckoSession session, @HideReason int reason);
+    }
+
     /**
      * This is used to send responses in delegate methods that have asynchronous responses.
      */
     public interface Response<T> {
         /**
          * @param val The value contained in the response
          */
         void respond(T val);