зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1416918 - 1. Add TextInputController; r=esawin
Add TextInputController and add a getter for it in GeckoSession. TextInputController is used to process key events and to interact with the input method manager. MozReview-Commit-ID: 1j2Moqukf8U --HG-- extra : rebase_source : 03fee51ba3e2034b8689c4fa6fe58f7432dc1ad4
This commit is contained in:
Родитель
5fedbb92b8
Коммит
6075204d7c
|
@ -451,6 +451,7 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
|
|||
'sqlite/MatrixBlobCursor.java',
|
||||
'sqlite/SQLiteBridge.java',
|
||||
'sqlite/SQLiteBridgeException.java',
|
||||
'TextInputController.java',
|
||||
'TouchEventInterceptor.java',
|
||||
'WakeLockDelegate.java',
|
||||
]]
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.mozilla.gecko.mozglue.JNIObject;
|
|||
import org.mozilla.gecko.util.BundleEventListener;
|
||||
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;
|
||||
|
@ -28,6 +29,7 @@ import android.os.IInterface;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
@ -65,6 +67,8 @@ public class GeckoSession extends LayerSession
|
|||
private final EventDispatcher mEventDispatcher =
|
||||
new EventDispatcher(mNativeQueue);
|
||||
|
||||
private final TextInputController mTextInput = new TextInputController(this);
|
||||
|
||||
private final GeckoSessionHandler<ContentListener> mContentHandler =
|
||||
new GeckoSessionHandler<ContentListener>(
|
||||
"GeckoViewContent", this,
|
||||
|
@ -328,6 +332,10 @@ public class GeckoSession extends LayerSession
|
|||
@WrapForJNI(dispatchTo = "proxy")
|
||||
public native void attach(GeckoView view);
|
||||
|
||||
@WrapForJNI(dispatchTo = "proxy")
|
||||
public native void attachEditable(IGeckoEditableParent parent,
|
||||
GeckoEditableChild child);
|
||||
|
||||
@WrapForJNI(calledFrom = "gecko")
|
||||
private synchronized void onReady() {
|
||||
if (mNativeQueue.checkAndSetState(State.INITIAL, State.READY)) {
|
||||
|
@ -471,6 +479,8 @@ public class GeckoSession extends LayerSession
|
|||
}
|
||||
|
||||
public void openWindow(final Context appContext) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
if (isOpen()) {
|
||||
throw new IllegalStateException("Session is open");
|
||||
}
|
||||
|
@ -501,6 +511,10 @@ public class GeckoSession extends LayerSession
|
|||
String.class, chromeUri,
|
||||
screenId, isPrivate);
|
||||
}
|
||||
|
||||
if (mTextInput != null) {
|
||||
mTextInput.onWindowReady(mNativeQueue, mWindow);
|
||||
}
|
||||
}
|
||||
|
||||
public void attachView(final GeckoView view) {
|
||||
|
@ -531,6 +545,16 @@ public class GeckoSession extends LayerSession
|
|||
mWindow = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TextInputController instance for this session.
|
||||
*
|
||||
* @return TextInputController instance.
|
||||
*/
|
||||
public @NonNull TextInputController getTextInputController() {
|
||||
// May be called on any thread.
|
||||
return mTextInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the given URI.
|
||||
* @param uri The URI of the resource to load.
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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.gecko;
|
||||
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
/**
|
||||
* TextInputController handles text input for GeckoSession through key events or input
|
||||
* methods. It is typically used to implement certain methods in View such as {@code
|
||||
* onCreateInputConnection()}, by forwarding such calls to corresponding methods in
|
||||
* TextInputController.
|
||||
*/
|
||||
public final class TextInputController {
|
||||
|
||||
/* package */ interface Delegate {
|
||||
View getView();
|
||||
Handler getHandler(Handler defHandler);
|
||||
InputConnection onCreateInputConnection(EditorInfo attrs);
|
||||
boolean onKeyPreIme(int keyCode, KeyEvent event);
|
||||
boolean onKeyDown(int keyCode, KeyEvent event);
|
||||
boolean onKeyUp(int keyCode, KeyEvent event);
|
||||
boolean onKeyLongPress(int keyCode, KeyEvent event);
|
||||
boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
|
||||
boolean isInputActive();
|
||||
}
|
||||
|
||||
private final GeckoSession mSession;
|
||||
private final GeckoEditable mEditable = new GeckoEditable();
|
||||
private final GeckoEditableChild mEditableChild = new GeckoEditableChild(mEditable);
|
||||
private Delegate mInputConnection;
|
||||
|
||||
/* package */ TextInputController(final @NonNull GeckoSession session) {
|
||||
mSession = session;
|
||||
mEditable.setDefaultEditableChild(mEditableChild);
|
||||
}
|
||||
|
||||
/* package */ void onWindowReady(final NativeQueue queue,
|
||||
final GeckoSession.Window window) {
|
||||
if (queue.isReady()) {
|
||||
window.attachEditable(mEditable, mEditableChild);
|
||||
} else {
|
||||
queue.queueUntilReady(window, "attachEditable",
|
||||
IGeckoEditableParent.class, mEditable,
|
||||
GeckoEditableChild.class, mEditableChild);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Handler for the background input method thread. In order to use a background
|
||||
* thread for input method operations on systems prior to Nougat, first override
|
||||
* {@code View.getHandler()} for the View returning the InputConnection instance, and
|
||||
* then call this method from the overridden method.
|
||||
*
|
||||
* For example: <pre>{@code
|
||||
* @Override
|
||||
* public Handler getHandler() {
|
||||
* if (Build.VERSION.SDK_INT >= 24) {
|
||||
* return super.getHandler();
|
||||
* }
|
||||
* return getSession().getTextInputController().getHandler(super.getHandler());
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param defHandler Handler returned by the system {@code getHandler} implementation.
|
||||
* @return Handler to return to the system through {@code getHandler}.
|
||||
*/
|
||||
public @NonNull Handler getHandler(final @NonNull Handler defHandler) {
|
||||
// May be called on any thread.
|
||||
if (mInputConnection != null) {
|
||||
return mInputConnection.getHandler(defHandler);
|
||||
}
|
||||
return defHandler;
|
||||
}
|
||||
|
||||
private synchronized void ensureInputConnection() {
|
||||
if (mInputConnection == null) {
|
||||
mInputConnection = GeckoInputConnection.create(mSession,
|
||||
/* view */ null,
|
||||
mEditable);
|
||||
mEditable.setListener((GeckoEditableListener) mInputConnection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current View for text input.
|
||||
*
|
||||
* @return Current text input View or null if not set.
|
||||
* @see #setView(View)
|
||||
*/
|
||||
public @Nullable View getView() {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
return mInputConnection != null ? mInputConnection.getView() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the View for text input. The current View is used to interact with the system
|
||||
* input method manager and to display certain text input UI elements.
|
||||
*
|
||||
* @param view Text input View or null to clear current View.
|
||||
*/
|
||||
public synchronized void setView(final @Nullable View view) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
if (view == null) {
|
||||
mInputConnection = null;
|
||||
} else if (mInputConnection == null || mInputConnection.getView() != view) {
|
||||
mInputConnection = GeckoInputConnection.create(mSession, view, mEditable);
|
||||
}
|
||||
mEditable.setListener((GeckoEditableListener) mInputConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an InputConnection instance. For full functionality, call {@link
|
||||
* #setView(View)} first before calling this method.
|
||||
*
|
||||
* @param outAttrs EditorInfo instance to be filled on return.
|
||||
* @return InputConnection instance or null if input method is not active.
|
||||
*/
|
||||
public synchronized @Nullable InputConnection onCreateInputConnection(
|
||||
final @NonNull EditorInfo attrs) {
|
||||
// May be called on any thread.
|
||||
ensureInputConnection();
|
||||
return mInputConnection.onCreateInputConnection(attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a KeyEvent as a pre-IME event.
|
||||
*
|
||||
* @param keyCode Key code.
|
||||
* @param event KeyEvent instance.
|
||||
* @return True if the event was handled.
|
||||
*/
|
||||
public boolean onKeyPreIme(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
ensureInputConnection();
|
||||
return mInputConnection.onKeyPreIme(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a KeyEvent as a key-down event.
|
||||
*
|
||||
* @param keyCode Key code.
|
||||
* @param event KeyEvent instance.
|
||||
* @return True if the event was handled.
|
||||
*/
|
||||
public boolean onKeyDown(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
ensureInputConnection();
|
||||
return mInputConnection.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a KeyEvent as a key-up event.
|
||||
*
|
||||
* @param keyCode Key code.
|
||||
* @param event KeyEvent instance.
|
||||
* @return True if the event was handled.
|
||||
*/
|
||||
public boolean onKeyUp(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
ensureInputConnection();
|
||||
return mInputConnection.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a KeyEvent as a long-press event.
|
||||
*
|
||||
* @param keyCode Key code.
|
||||
* @param event KeyEvent instance.
|
||||
* @return True if the event was handled.
|
||||
*/
|
||||
public boolean onKeyLongPress(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
ensureInputConnection();
|
||||
return mInputConnection.onKeyLongPress(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a KeyEvent as a multiple-press event.
|
||||
*
|
||||
* @param keyCode Key code.
|
||||
* @param event KeyEvent instance.
|
||||
* @return True if the event was handled.
|
||||
*/
|
||||
public boolean onKeyMultiple(final int keyCode, final int repeatCount,
|
||||
final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
ensureInputConnection();
|
||||
return mInputConnection.onKeyMultiple(keyCode, repeatCount, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there is an active input connection, usually as a result of a
|
||||
* focused input field.
|
||||
*
|
||||
* @return True if input is active.
|
||||
*/
|
||||
public boolean isInputActive() {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
return mInputConnection != null && mInputConnection.isInputActive();
|
||||
}
|
||||
}
|
|
@ -301,6 +301,10 @@ public:
|
|||
void Attach(const GeckoSession::Window::LocalRef& inst,
|
||||
jni::Object::Param aView);
|
||||
|
||||
void AttachEditable(const GeckoSession::Window::LocalRef& inst,
|
||||
jni::Object::Param aEditableParent,
|
||||
jni::Object::Param aEditableChild);
|
||||
|
||||
void EnableEventDispatcher();
|
||||
};
|
||||
|
||||
|
@ -1227,10 +1231,10 @@ EGLSurface nsWindow::PMPMSupport::sSurface;
|
|||
nsWindow::GeckoViewSupport::~GeckoViewSupport()
|
||||
{
|
||||
// Disassociate our GeckoEditable instance with our native object.
|
||||
MOZ_ASSERT(window.mEditableSupport && window.mEditable);
|
||||
if (window.mEditableSupport) {
|
||||
window.mEditableSupport.Detach();
|
||||
window.mEditable->OnViewChange(nullptr);
|
||||
window.mEditable = nullptr;
|
||||
window.mEditableParent = nullptr;
|
||||
}
|
||||
|
||||
if (window.mNPZCSupport) {
|
||||
window.mNPZCSupport.Detach();
|
||||
|
@ -1302,13 +1306,6 @@ nsWindow::GeckoViewSupport::Open(const jni::Class::LocalRef& aCls,
|
|||
window->mGeckoViewSupport->Transfer(
|
||||
sessionWindow, aCompositor, aDispatcher, aSettings);
|
||||
|
||||
// Attach a new GeckoEditable support object to the new window.
|
||||
auto editable = GeckoEditable::New();
|
||||
auto editableChild = GeckoEditableChild::New(editable);
|
||||
editable->SetDefaultEditableChild(editableChild);
|
||||
window->mEditable = editable;
|
||||
window->mEditableSupport.Attach(editableChild, window, editableChild);
|
||||
|
||||
if (window->mWidgetListener) {
|
||||
nsCOMPtr<nsIXULWindow> xulWindow(
|
||||
window->mWidgetListener->GetXULWindow());
|
||||
|
@ -1378,9 +1375,19 @@ void
|
|||
nsWindow::GeckoViewSupport::Attach(const GeckoSession::Window::LocalRef& inst,
|
||||
jni::Object::Param aView)
|
||||
{
|
||||
// Associate our previous GeckoEditable with the new GeckoView.
|
||||
MOZ_ASSERT(window.mEditable);
|
||||
window.mEditable->OnViewChange(aView);
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::GeckoViewSupport::AttachEditable(const GeckoSession::Window::LocalRef& inst,
|
||||
jni::Object::Param aEditableParent,
|
||||
jni::Object::Param aEditableChild)
|
||||
{
|
||||
java::GeckoEditableChild::LocalRef editableChild(inst.Env());
|
||||
editableChild = java::GeckoEditableChild::Ref::From(aEditableChild);
|
||||
|
||||
MOZ_ASSERT(!window.mEditableSupport);
|
||||
window.mEditableSupport.Attach(editableChild, &window, editableChild);
|
||||
window.mEditableParent = aEditableParent;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -184,7 +184,7 @@ private:
|
|||
// Object that implements native GeckoEditable calls.
|
||||
// Strong referenced by the Java instance.
|
||||
NativePtr<mozilla::widget::GeckoEditableSupport> mEditableSupport;
|
||||
mozilla::java::GeckoEditable::GlobalRef mEditable;
|
||||
mozilla::jni::Object::GlobalRef mEditableParent;
|
||||
|
||||
class GeckoViewSupport;
|
||||
// Object that implements native GeckoView calls and associated states.
|
||||
|
@ -309,7 +309,7 @@ public:
|
|||
// event (like a keypress or mouse click).
|
||||
void UserActivity();
|
||||
|
||||
mozilla::java::GeckoEditable::Ref& GetEditableParent() { return mEditable; }
|
||||
mozilla::jni::Object::Ref& GetEditableParent() { return mEditableParent; }
|
||||
|
||||
void RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage) override;
|
||||
void UpdateRootFrameMetrics(const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom) override;
|
||||
|
|
Загрузка…
Ссылка в новой задаче