diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-06-30 15:32:54 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-06-30 15:32:54 -0700 |
commit | 7f6c6e8ffa3c6e036bf8037012a930e52f54e296 (patch) | |
tree | 1b3319e049eaf96e5783350e33894d830f41e6e4 | |
parent | b2d1a71bcc7d40f2a6ca06ddcc92e3cd2a135d1e (diff) | |
parent | 3c80a4a044865bdf1289c7896baffa1c082d835c (diff) | |
download | frameworks_base-7f6c6e8ffa3c6e036bf8037012a930e52f54e296.zip frameworks_base-7f6c6e8ffa3c6e036bf8037012a930e52f54e296.tar.gz frameworks_base-7f6c6e8ffa3c6e036bf8037012a930e52f54e296.tar.bz2 |
am 3c80a4a0: Implement default key handling for native code.
Merge commit '3c80a4a044865bdf1289c7896baffa1c082d835c' into gingerbread-plus-aosp
* commit '3c80a4a044865bdf1289c7896baffa1c082d835c':
Implement default key handling for native code.
-rw-r--r-- | core/java/android/app/Activity.java | 1 | ||||
-rw-r--r-- | core/java/android/app/NativeActivity.java | 62 | ||||
-rwxr-xr-x | core/java/android/view/KeyEvent.java | 36 | ||||
-rw-r--r-- | core/jni/android_app_NativeActivity.cpp | 137 | ||||
-rw-r--r-- | core/jni/android_view_KeyEvent.cpp | 23 | ||||
-rw-r--r-- | include/ui/Input.h | 10 | ||||
-rw-r--r-- | include/ui/InputTransport.h | 4 | ||||
-rw-r--r-- | libs/ui/Input.cpp | 64 | ||||
-rw-r--r-- | native/android/input.cpp | 9 |
9 files changed, 291 insertions, 55 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9b9ae52..985f591 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -39,7 +39,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.text.Selection; import android.text.SpannableStringBuilder; diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 429d164..d43368b 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -6,9 +6,13 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; +import android.os.Looper; +import android.os.MessageQueue; import android.view.InputChannel; import android.view.InputQueue; +import android.view.KeyEvent; import android.view.SurfaceHolder; +import android.view.View; import java.io.File; @@ -22,7 +26,12 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, private int mNativeHandle; - private native int loadNativeCode(String path); + private InputQueue mCurInputQueue; + private SurfaceHolder mCurSurfaceHolder; + + private boolean mDestroyed; + + private native int loadNativeCode(String path, MessageQueue queue); private native void unloadNativeCode(int handle); private native void onStartNative(int handle); @@ -78,7 +87,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, throw new IllegalArgumentException("Unable to find native library: " + libname); } - mNativeHandle = loadNativeCode(path); + mNativeHandle = loadNativeCode(path, Looper.myQueue()); if (mNativeHandle == 0) { throw new IllegalArgumentException("Unable to load native library: " + path); } @@ -87,6 +96,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, @Override protected void onDestroy() { + mDestroyed = true; + if (mCurSurfaceHolder != null) { + onSurfaceDestroyedNative(mNativeHandle, mCurSurfaceHolder); + mCurSurfaceHolder = null; + } + if (mCurInputQueue != null) { + onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel()); + mCurInputQueue = null; + } unloadNativeCode(mNativeHandle); super.onDestroy(); } @@ -124,32 +142,58 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, @Override public void onLowMemory() { super.onLowMemory(); - onLowMemoryNative(mNativeHandle); + if (!mDestroyed) { + onLowMemoryNative(mNativeHandle); + } } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - onWindowFocusChangedNative(mNativeHandle, hasFocus); + if (!mDestroyed) { + onWindowFocusChangedNative(mNativeHandle, hasFocus); + } } public void surfaceCreated(SurfaceHolder holder) { - onSurfaceCreatedNative(mNativeHandle, holder); + if (!mDestroyed) { + mCurSurfaceHolder = holder; + onSurfaceCreatedNative(mNativeHandle, holder); + } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - onSurfaceChangedNative(mNativeHandle, holder, format, width, height); + if (!mDestroyed) { + mCurSurfaceHolder = holder; + onSurfaceChangedNative(mNativeHandle, holder, format, width, height); + } } public void surfaceDestroyed(SurfaceHolder holder) { - onSurfaceDestroyedNative(mNativeHandle, holder); + mCurSurfaceHolder = null; + if (!mDestroyed) { + onSurfaceDestroyedNative(mNativeHandle, holder); + } } public void onInputQueueCreated(InputQueue queue) { - onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel()); + if (!mDestroyed) { + mCurInputQueue = queue; + onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel()); + } } public void onInputQueueDestroyed(InputQueue queue) { - onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel()); + mCurInputQueue = null; + if (!mDestroyed) { + onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel()); + } + } + + void dispatchUnhandledKeyEvent(KeyEvent event) { + View decor = getWindow().getDecorView(); + if (decor != null) { + decor.dispatchKeyEvent(event); + } } } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index ae9746e..0bfb6d6 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -676,33 +676,12 @@ public class KeyEvent implements Parcelable { * TODO: should the dpad keys be here? arguably, because they also shouldn't be menu shortcuts */ public final boolean isSystem() { - switch (mKeyCode) { - case KEYCODE_MENU: - case KEYCODE_SOFT_RIGHT: - case KEYCODE_HOME: - case KEYCODE_BACK: - case KEYCODE_CALL: - case KEYCODE_ENDCALL: - case KEYCODE_VOLUME_UP: - case KEYCODE_VOLUME_DOWN: - case KEYCODE_MUTE: - case KEYCODE_POWER: - case KEYCODE_HEADSETHOOK: - case KEYCODE_MEDIA_PLAY_PAUSE: - case KEYCODE_MEDIA_STOP: - case KEYCODE_MEDIA_NEXT: - case KEYCODE_MEDIA_PREVIOUS: - case KEYCODE_MEDIA_REWIND: - case KEYCODE_MEDIA_FAST_FORWARD: - case KEYCODE_CAMERA: - case KEYCODE_FOCUS: - case KEYCODE_SEARCH: - case KEYCODE_PICTSYMBOLS: - case KEYCODE_SWITCH_CHARSET: - return true; - default: - return false; - } + return native_isSystemKey(mKeyCode); + } + + /** @hide */ + public final boolean hasDefaultAction() { + return native_hasDefaultAction(mKeyCode); } @@ -1226,4 +1205,7 @@ public class KeyEvent implements Parcelable { mDownTime = in.readLong(); mEventTime = in.readLong(); } + + private native boolean native_isSystemKey(int keyCode); + private native boolean native_hasDefaultAction(int keyCode); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index a22b353..dd59d63 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -17,17 +17,63 @@ #define LOG_TAG "NativeActivity" #include <utils/Log.h> -#include "JNIHelp.h" -#include "android_view_InputChannel.h" +#include <poll.h> +#include <dlfcn.h> + #include <android_runtime/AndroidRuntime.h> #include <android/native_activity.h> #include <ui/InputTransport.h> +#include <utils/PollLoop.h> -#include <dlfcn.h> +#include "JNIHelp.h" +#include "android_os_MessageQueue.h" +#include "android_view_InputChannel.h" +#include "android_view_KeyEvent.h" namespace android { +static struct { + jclass clazz; + + jmethodID dispatchUnhandledKeyEvent; +} gNativeActivityClassInfo; + +struct MyInputQueue : AInputQueue { + explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite) + : AInputQueue(channel), mWorkWrite(workWrite) { + } + + virtual void doDefaultKey(android::KeyEvent* keyEvent) { + mLock.lock(); + LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); + if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { + int8_t cmd = 1; + write(mWorkWrite, &cmd, sizeof(cmd)); + } + mPendingKeys.add(keyEvent); + mLock.unlock(); + } + + KeyEvent* getNextEvent() { + KeyEvent* event = NULL; + + mLock.lock(); + if (mPendingKeys.size() > 0) { + event = mPendingKeys[0]; + mPendingKeys.removeAt(0); + } + mLock.unlock(); + + return event; + } + + int mWorkWrite; + + Mutex mLock; + Vector<KeyEvent*> mPendingKeys; +}; + struct NativeCode { NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { memset(&activity, sizeof(activity), 0); @@ -37,14 +83,26 @@ struct NativeCode { surface = NULL; inputChannel = NULL; nativeInputQueue = NULL; + mainWorkRead = mainWorkWrite = -1; } ~NativeCode() { + if (activity.env != NULL && activity.clazz != NULL) { + activity.env->DeleteGlobalRef(activity.clazz); + } + if (pollLoop != NULL && mainWorkRead >= 0) { + pollLoop->removeCallback(mainWorkRead); + } + if (nativeInputQueue != NULL) { + nativeInputQueue->mWorkWrite = -1; + } setSurface(NULL); setInputChannel(NULL); if (callbacks.onDestroy != NULL) { callbacks.onDestroy(&activity); } + if (mainWorkRead >= 0) close(mainWorkRead); + if (mainWorkWrite >= 0) close(mainWorkWrite); if (dlhandle != NULL) { dlclose(dlhandle); } @@ -73,7 +131,7 @@ struct NativeCode { sp<InputChannel> ic = android_view_InputChannel_getInputChannel(activity.env, _channel); if (ic != NULL) { - nativeInputQueue = new AInputQueue(ic); + nativeInputQueue = new MyInputQueue(ic, mainWorkWrite); if (nativeInputQueue->getConsumer().initialize() != android::OK) { delete nativeInputQueue; nativeInputQueue = NULL; @@ -94,11 +152,36 @@ struct NativeCode { jobject surface; jobject inputChannel; - struct AInputQueue* nativeInputQueue; + struct MyInputQueue* nativeInputQueue; + + // These are used to wake up the main thread to process work. + int mainWorkRead; + int mainWorkWrite; + sp<PollLoop> pollLoop; }; +static bool mainWorkCallback(int fd, int events, void* data) { + NativeCode* code = (NativeCode*)data; + if ((events & POLLIN) != 0) { + KeyEvent* keyEvent; + while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) { + jobject inputEventObj = android_view_KeyEvent_fromNative( + code->activity.env, keyEvent); + code->activity.env->CallVoidMethod(code->activity.clazz, + gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); + int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal(); + if (res != OK) { + LOGW("Failed to send finished signal on channel '%s'. status=%d", + code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res); + } + } + } + + return true; +} + static jint -loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path) +loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue) { const char* pathStr = env->GetStringUTFChars(path, NULL); NativeCode* code = NULL; @@ -115,6 +198,24 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path) delete code; return 0; } + + code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue); + if (code->pollLoop == NULL) { + LOGW("Unable to retrieve MessageQueue's PollLoop"); + delete code; + return 0; + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGW("could not create pipe: %s", strerror(errno)); + delete code; + return 0; + } + code->mainWorkRead = msgpipe[0]; + code->mainWorkWrite = msgpipe[1]; + code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); + code->activity.callbacks = &code->callbacks; if (env->GetJavaVM(&code->activity.vm) < 0) { LOGW("NativeActivity GetJavaVM failed"); @@ -122,7 +223,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path) return 0; } code->activity.env = env; - code->activity.clazz = clazz; + code->activity.clazz = env->NewGlobalRef(clazz); code->createActivityFunc(&code->activity, NULL, 0); } @@ -288,7 +389,7 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject } static const JNINativeMethod g_methods[] = { - { "loadNativeCode", "(Ljava/lang/String;)I", (void*)loadNativeCode_native }, + { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;)I", (void*)loadNativeCode_native }, { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, { "onStartNative", "(I)V", (void*)onStart_native }, { "onResumeNative", "(I)V", (void*)onResume_native }, @@ -306,15 +407,25 @@ static const JNINativeMethod g_methods[] = { static const char* const kNativeActivityPathName = "android/app/NativeActivity"; +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + int register_android_app_NativeActivity(JNIEnv* env) { //LOGD("register_android_app_NativeActivity"); - jclass clazz; - - clazz = env->FindClass(kNativeActivityPathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.NativeActivity"); - + FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName); + + GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, + gNativeActivityClassInfo.clazz, + "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V"); + return AndroidRuntime::registerNativeMethods( env, kNativeActivityPathName, g_methods, NELEM(g_methods)); diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index df3b952..8f648f4 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -76,8 +76,23 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t natur milliseconds_to_nanoseconds(eventTime)); } +static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) { + return KeyEvent::isSystemKey(keyCode); +} + +static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode) { + return KeyEvent::hasDefaultAction(keyCode); +} + // ---------------------------------------------------------------------------- +static const JNINativeMethod g_methods[] = { + { "native_isSystemKey", "(I)Z", (void*)native_isSystemKey }, + { "native_hasDefaultAction", "(I)Z", (void*)native_hasDefaultAction }, +}; + +static const char* const kKeyEventPathName = "android/view/KeyEvent"; + #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ LOG_FATAL_IF(! var, "Unable to find class " className); \ @@ -92,8 +107,8 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t natur LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_view_KeyEvent(JNIEnv* env) { - FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); - + FIND_CLASS(gKeyEventClassInfo.clazz, kKeyEventPathName); + GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz, "<init>", "(JJIIIIIII)V"); @@ -118,7 +133,9 @@ int register_android_view_KeyEvent(JNIEnv* env) { GET_FIELD_ID(gKeyEventClassInfo.mCharacters, gKeyEventClassInfo.clazz, "mCharacters", "Ljava/lang/String;"); - return 0; + return AndroidRuntime::registerNativeMethods( + env, kKeyEventPathName, + g_methods, NELEM(g_methods)); } } // namespace android diff --git a/include/ui/Input.h b/include/ui/Input.h index 8890789..214f587 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -145,7 +145,7 @@ public: inline int32_t getDeviceId() const { return mDeviceId; } inline int32_t getNature() const { return mNature; } - + protected: void initialize(int32_t deviceId, int32_t nature); @@ -179,6 +179,14 @@ public: inline nsecs_t getEventTime() const { return mEventTime; } + // Return true if this event may have a default action implementation. + static bool hasDefaultAction(int32_t keyCode); + bool hasDefaultAction() const; + + // Return true if this event represents a system key. + static bool isSystemKey(int32_t keyCode); + bool isSystemKey() const; + void initialize( int32_t deviceId, int32_t nature, diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 4e76051..2dfe2a8 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -339,12 +339,14 @@ public: explicit AInputQueue(const android::sp<android::InputChannel>& channel); /* Destroys the consumer and releases its input channel. */ - ~AInputQueue(); + virtual ~AInputQueue(); inline android::InputConsumer& getConsumer() { return mConsumer; } android::status_t consume(android::InputEvent** event); + virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0; + private: android::InputConsumer mConsumer; android::PreallocatedInputEventFactory mInputEventFactory; diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 4121b5a..a64251f 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -20,6 +20,70 @@ void InputEvent::initialize(int32_t deviceId, int32_t nature) { // class KeyEvent +bool KeyEvent::hasDefaultAction(int32_t keyCode) { + switch (keyCode) { + case KEYCODE_HOME: + case KEYCODE_BACK: + case KEYCODE_CALL: + case KEYCODE_ENDCALL: + case KEYCODE_VOLUME_UP: + case KEYCODE_VOLUME_DOWN: + case KEYCODE_POWER: + case KEYCODE_CAMERA: + case KEYCODE_HEADSETHOOK: + case KEYCODE_MENU: + case KEYCODE_NOTIFICATION: + case KEYCODE_FOCUS: + case KEYCODE_SEARCH: + case KEYCODE_MEDIA_PLAY_PAUSE: + case KEYCODE_MEDIA_STOP: + case KEYCODE_MEDIA_NEXT: + case KEYCODE_MEDIA_PREVIOUS: + case KEYCODE_MEDIA_REWIND: + case KEYCODE_MEDIA_FAST_FORWARD: + case KEYCODE_MUTE: + return true; + } + + return false; +} + +bool KeyEvent::hasDefaultAction() const { + return hasDefaultAction(getKeyCode()); +} + +bool KeyEvent::isSystemKey(int32_t keyCode) { + switch (keyCode) { + case KEYCODE_MENU: + case KEYCODE_SOFT_RIGHT: + case KEYCODE_HOME: + case KEYCODE_BACK: + case KEYCODE_CALL: + case KEYCODE_ENDCALL: + case KEYCODE_VOLUME_UP: + case KEYCODE_VOLUME_DOWN: + case KEYCODE_MUTE: + case KEYCODE_POWER: + case KEYCODE_HEADSETHOOK: + case KEYCODE_MEDIA_PLAY_PAUSE: + case KEYCODE_MEDIA_STOP: + case KEYCODE_MEDIA_NEXT: + case KEYCODE_MEDIA_PREVIOUS: + case KEYCODE_MEDIA_REWIND: + case KEYCODE_MEDIA_FAST_FORWARD: + case KEYCODE_CAMERA: + case KEYCODE_FOCUS: + case KEYCODE_SEARCH: + return true; + } + + return false; +} + +bool KeyEvent::isSystemKey() const { + return isSystemKey(getKeyCode()); +} + void KeyEvent::initialize( int32_t deviceId, int32_t nature, diff --git a/native/android/input.cpp b/native/android/input.cpp index e0544a1..8498840 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -225,6 +225,15 @@ int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) { void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) { + if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY + && ((KeyEvent*)event)->hasDefaultAction()) { + // The app didn't handle this, but it may have a default action + // associated with it. We need to hand this back to Java to be + // executed. + queue->doDefaultKey((KeyEvent*)event); + return; + } + int32_t res = queue->getConsumer().sendFinishedSignal(); if (res != android::OK) { LOGW("Failed to send finished signal on channel '%s'. status=%d", |