Bug 599811 - Use KeyListener to filter keyevents, r=dougt a=blocking-fennec
authorMichael Wu <mwu@mozilla.com>
Mon, 22 Nov 2010 22:56:37 -0800
changeset 58020 0b2b0417c11f7ecdc7e0baad92a8149790696613
parent 58019 6ed1bf8ddeec122096e4a261dd621267a89675da
child 58021 5c0802167d09276227e0239cb38793336ca3be7b
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersdougt, blocking-fennec
bugs599811
milestone2.0b8pre
Bug 599811 - Use KeyListener to filter keyevents, r=dougt a=blocking-fennec
embedding/android/GeckoApp.java
embedding/android/GeckoInputConnection.java
embedding/android/GeckoSurfaceView.java
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -344,30 +344,37 @@ abstract public class GeckoApp
                     surfaceView.inputConnection != null &&
                     surfaceView.inputConnection.onKeyDel()) {
                     return true;
                 }
                 break;
             default:
                 break;
         }
-        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+        // KeyListener returns true if it handled the event for us.
+        if (GeckoApp.surfaceView.mIMEState == GeckoSurfaceView.IME_STATE_DISABLED ||
+            keyCode == KeyEvent.KEYCODE_ENTER ||
+            !GeckoApp.surfaceView.mKeyListener.onKeyUp(GeckoApp.surfaceView, GeckoApp.surfaceView.mEditable, keyCode, event))
+            GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
         return true;
     }
 
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK:
                 if (!event.isTracking() || event.isCanceled())
                     return false;
                 break;
             default:
                 break;
         }
-        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+        if (GeckoApp.surfaceView.mIMEState == GeckoSurfaceView.IME_STATE_DISABLED ||
+            keyCode == KeyEvent.KEYCODE_ENTER ||
+            !GeckoApp.surfaceView.mKeyListener.onKeyDown(GeckoApp.surfaceView, GeckoApp.surfaceView.mEditable, keyCode, event))
+            GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
         return true;
     }
 
     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
         GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
         return true;
     }
 
--- a/embedding/android/GeckoInputConnection.java
+++ b/embedding/android/GeckoInputConnection.java
@@ -50,16 +50,17 @@ import android.view.*;
 import android.view.inputmethod.*;
 import android.content.*;
 import android.R;
 
 import android.util.*;
 
 public class GeckoInputConnection
     extends BaseInputConnection
+    implements TextWatcher
 {
     public GeckoInputConnection (View targetView) {
         super(targetView, true);
         mQueryResult = new SynchronousQueue<String>();
     }
 
     @Override
     public boolean beginBatchEdit() {
@@ -481,16 +482,19 @@ public class GeckoInputConnection
         commitText(null, 1);
         return true;
     }
 
     public void notifyTextChange(InputMethodManager imm, String text,
                                  int start, int oldEnd, int newEnd) {
         //Log.d("GeckoAppJava", "IME: notifyTextChange");
 
+        if (!text.contentEquals(GeckoApp.surfaceView.mEditable))
+            GeckoApp.surfaceView.setupEditable(text);
+
         if (mUpdateRequest == null)
             return;
 
         mUpdateExtract.flags = 0;
         mUpdateExtract.partialStartOffset = 0;
         mUpdateExtract.partialEndOffset = oldEnd;
 
         // Faster to not query for selection
@@ -511,24 +515,53 @@ public class GeckoInputConnection
         if (mComposing)
             imm.updateSelection(GeckoApp.surfaceView,
                 mCompositionStart + mCompositionSelStart,
                 mCompositionStart + mCompositionSelStart + mCompositionSelLen,
                 mCompositionStart,
                 mCompositionStart + mComposingText.length());
         else
             imm.updateSelection(GeckoApp.surfaceView, start, end, -1, -1);
+
+        int maxLen = GeckoApp.surfaceView.mEditable.length();
+        Selection.setSelection(GeckoApp.surfaceView.mEditable, 
+                               Math.min(start, maxLen),
+                               Math.min(end, maxLen));
     }
 
     public void reset() {
         mComposing = false;
         mComposingText = null;
         mUpdateRequest = null;
     }
 
+    // TextWatcher
+    public void onTextChanged(CharSequence s, int start, int before, int count)
+    {
+        GeckoAppShell.sendEventToGecko(
+            new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start, before));
+
+        if (count == 0)
+            GeckoAppShell.sendEventToGecko(
+                new GeckoEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0));
+        else
+            GeckoAppShell.sendEventToGecko(
+                new GeckoEvent(0, count,
+                               GeckoEvent.IME_RANGE_RAWINPUT, 0, 0, 0,
+                               s.subSequence(start, start + count).toString()));
+    }
+
+    public void afterTextChanged(Editable s)
+    {
+    }
+
+    public void beforeTextChanged(CharSequence s, int start, int count, int after)
+    {
+    }
+
     // Is a composition active?
     boolean mComposing;
     // Composition text when a composition is active
     String mComposingText;
     // Start index of the composition within the text body
     int mCompositionStart;
     /* During a composition, we should not alter the real selection,
         therefore we keep our own offsets to emulate selection */
--- a/embedding/android/GeckoSurfaceView.java
+++ b/embedding/android/GeckoSurfaceView.java
@@ -43,16 +43,17 @@ import java.util.concurrent.*;
 import java.util.concurrent.locks.*;
 import java.util.concurrent.atomic.*;
 import java.util.zip.*;
 import java.nio.*;
 
 import android.os.*;
 import android.app.*;
 import android.text.*;
+import android.text.method.*;
 import android.view.*;
 import android.view.inputmethod.*;
 import android.content.*;
 import android.graphics.*;
 import android.widget.*;
 import android.hardware.*;
 import android.location.*;
 
@@ -78,16 +79,18 @@ class GeckoSurfaceView
 
         mWidth = 0;
         mHeight = 0;
         mBufferWidth = 0;
         mBufferHeight = 0;
 
         mSurfaceLock = new ReentrantLock();
 
+        mEditableFactory = Editable.Factory.getInstance();
+        setupEditable("");
         mIMEState = IME_STATE_DISABLED;
         mIMEHint = "";
     }
 
     protected void finalize() throws Throwable {
         super.finalize();
     }
 
@@ -284,16 +287,17 @@ class GeckoSurfaceView
     public boolean onCheckIsTextEditor () {
         return false;
     }
 
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
         outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
         outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
+        mKeyListener = TextKeyListener.getInstance();
 
         if (mIMEState == IME_STATE_PASSWORD)
             outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
         else if (mIMEHint.equalsIgnoreCase("url"))
             outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
         else if (mIMEHint.equalsIgnoreCase("email"))
             outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
         else if (mIMEHint.equalsIgnoreCase("search"))
@@ -313,16 +317,23 @@ class GeckoSurfaceView
         else if (mIMEHint.equalsIgnoreCase("time"))
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
                                  InputType.TYPE_DATETIME_VARIATION_TIME;
 
         inputConnection.reset();
         return inputConnection;
     }
 
+    public void setupEditable(String contents)
+    {
+        mEditable = mEditableFactory.newEditable(contents);
+        mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        Selection.setSelection(mEditable, contents.length());
+    }
+
     // accelerometer
     public void onAccuracyChanged(Sensor sensor, int accuracy)
     {
     }
 
     public void onSensorChanged(SensorEvent event)
     {
         GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
@@ -383,18 +394,22 @@ class GeckoSurfaceView
     int mBufferHeight;
 
     // IME stuff
     public static final int IME_STATE_DISABLED = 0;
     public static final int IME_STATE_ENABLED = 1;
     public static final int IME_STATE_PASSWORD = 2;
 
     GeckoInputConnection inputConnection;
+    KeyListener mKeyListener;
+    Editable mEditable;
+    Editable.Factory mEditableFactory;
     boolean mIMEFocus;
     int mIMEState;
     String mIMEHint;
 
     // Software rendering
     ByteBuffer mSoftwareBuffer;
     Bitmap mSoftwareBitmap;
 
     final SynchronousQueue<ByteBuffer> mSyncBuf = new SynchronousQueue<ByteBuffer>();
 }
+