diff --git a/api/current.xml b/api/current.xml index e38818c97aedc..0a553674b84b1 100644 --- a/api/current.xml +++ b/api/current.xml @@ -26365,7 +26365,9 @@ > - + + + + + + + + + + + + + + + + + + + + + - + #include +#include #include #include @@ -33,6 +34,9 @@ #include "android_view_InputChannel.h" #include "android_view_KeyEvent.h" +//#define LOG_TRACE(...) +#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) + namespace android { @@ -42,6 +46,8 @@ static struct { jmethodID dispatchUnhandledKeyEvent; jmethodID setWindowFlags; jmethodID setWindowFormat; + jmethodID showIme; + jmethodID hideIme; } gNativeActivityClassInfo; // ------------------------------------------------------------------------ @@ -56,6 +62,8 @@ enum { CMD_DEF_KEY = 1, CMD_SET_WINDOW_FORMAT, CMD_SET_WINDOW_FLAGS, + CMD_SHOW_SOFT_INPUT, + CMD_HIDE_SOFT_INPUT, }; static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { @@ -64,6 +72,8 @@ static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { work.arg1 = arg1; work.arg2 = arg2; + LOG_TRACE("write_work: cmd=%d", cmd); + restart: int res = write(fd, &work, sizeof(work)); if (res < 0 && errno == EINTR) { @@ -88,43 +98,177 @@ static bool read_work(int fd, ActivityWork* outWork) { // ------------------------------------------------------------------------ -/* - * Specialized input queue that allows unhandled key events to be dispatched - * back to the native activity's Java framework code. - */ -struct MyInputQueue : AInputQueue { - explicit MyInputQueue(const android::sp& channel, int workWrite) - : AInputQueue(channel), mWorkWrite(workWrite) { +} // namespace android + +using namespace android; + +AInputQueue::AInputQueue(const sp& channel, int workWrite) : + mWorkWrite(workWrite), mConsumer(channel) { + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGW("could not create pipe: %s", strerror(errno)); + mDispatchKeyRead = mDispatchKeyWrite = -1; + } else { + mDispatchKeyRead = msgpipe[0]; + mDispatchKeyWrite = msgpipe[1]; + int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK); + SLOGW_IF(result != 0, "Could not make AInputQueue read pipe " + "non-blocking: %s", strerror(errno)); + result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK); + SLOGW_IF(result != 0, "Could not make AInputQueue write pipe " + "non-blocking: %s", strerror(errno)); } +} + +AInputQueue::~AInputQueue() { + close(mDispatchKeyRead); + close(mDispatchKeyWrite); +} + +void AInputQueue::attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data) { + mPollLoop = static_cast(looper); + mPollLoop->setLooperCallback(mConsumer.getChannel()->getReceivePipeFd(), + POLLIN, callback, data); + mPollLoop->setLooperCallback(mDispatchKeyRead, + POLLIN, callback, data); +} + +void AInputQueue::detachLooper() { + mPollLoop->removeCallback(mConsumer.getChannel()->getReceivePipeFd()); + mPollLoop->removeCallback(mDispatchKeyRead); +} + +int32_t AInputQueue::hasEvents() { + struct pollfd pfd[2]; + + pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd(); + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = mDispatchKeyRead; + pfd[0].events = POLLIN; + pfd[0].revents = 0; - virtual void doDefaultKey(android::KeyEvent* keyEvent) { + int nfd = poll(pfd, 2, 0); + if (nfd <= 0) return 0; + return (pfd[0].revents == POLLIN || pfd[1].revents == POLLIN) ? 1 : -1; +} + +int32_t AInputQueue::getEvent(AInputEvent** outEvent) { + *outEvent = NULL; + + char byteread; + ssize_t nRead = read(mDispatchKeyRead, &byteread, 1); + if (nRead == 1) { mLock.lock(); - LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); - if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { - write_work(mWorkWrite, CMD_DEF_KEY); - } - mPendingKeys.add(keyEvent); - mLock.unlock(); - } - - KeyEvent* getNextEvent() { - KeyEvent* event = NULL; - - mLock.lock(); - if (mPendingKeys.size() > 0) { - event = mPendingKeys[0]; - mPendingKeys.removeAt(0); + if (mDispatchingKeys.size() > 0) { + KeyEvent* kevent = mDispatchingKeys[0]; + *outEvent = kevent; + mDispatchingKeys.removeAt(0); + mDeliveringKeys.add(kevent); } mLock.unlock(); - - return event; + if (*outEvent != NULL) { + return 0; + } } - int mWorkWrite; + int32_t res = mConsumer.receiveDispatchSignal(); + if (res != android::OK) { + LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", + mConsumer.getChannel()->getName().string(), res); + return -1; + } + + InputEvent* myEvent = NULL; + res = mConsumer.consume(&mInputEventFactory, &myEvent); + if (res != android::OK) { + LOGW("channel '%s' ~ Failed to consume input event. status=%d", + mConsumer.getChannel()->getName().string(), res); + mConsumer.sendFinishedSignal(); + return -1; + } + + *outEvent = myEvent; + return 0; +} + +void AInputQueue::finishEvent(AInputEvent* event, bool handled) { + bool needFinished = true; + + 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. + doDefaultKey((KeyEvent*)event); + needFinished = false; + } + + const size_t N = mDeliveringKeys.size(); + for (size_t i=0; i mPendingKeys; -}; + if (needFinished) { + int32_t res = mConsumer.sendFinishedSignal(); + if (res != android::OK) { + LOGW("Failed to send finished signal on channel '%s'. status=%d", + mConsumer.getChannel()->getName().string(), res); + } + } +} + +void AInputQueue::dispatchEvent(android::KeyEvent* event) { + mLock.lock(); + LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(), + mDispatchKeyWrite); + mDispatchingKeys.add(event); + mLock.unlock(); + +restart: + char dummy = 0; + int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy)); + if (res < 0 && errno == EINTR) { + goto restart; + } + + if (res == sizeof(dummy)) return; + + if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno)); + else LOGW("Truncated writing to dispatch fd: %d", res); +} + +KeyEvent* AInputQueue::consumeUnhandledEvent() { + KeyEvent* event = NULL; + + mLock.lock(); + if (mPendingKeys.size() > 0) { + event = mPendingKeys[0]; + mPendingKeys.removeAt(0); + } + mLock.unlock(); + + LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event); + + return event; +} + +void AInputQueue::doDefaultKey(KeyEvent* keyEvent) { + mLock.lock(); + LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); + if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { + write_work(mWorkWrite, CMD_DEF_KEY); + } + mPendingKeys.add(keyEvent); + mLock.unlock(); +} + +namespace android { // ------------------------------------------------------------------------ @@ -133,8 +277,8 @@ struct MyInputQueue : AInputQueue { */ struct NativeCode : public ANativeActivity { NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { - memset((ANativeActivity*)this, sizeof(ANativeActivity), 0); - memset(&callbacks, sizeof(callbacks), 0); + memset((ANativeActivity*)this, 0, sizeof(ANativeActivity)); + memset(&callbacks, 0, sizeof(callbacks)); dlhandle = _dlhandle; createActivityFunc = _createFunc; nativeWindow = NULL; @@ -188,7 +332,7 @@ struct NativeCode : public ANativeActivity { sp ic = android_view_InputChannel_getInputChannel(env, _channel); if (ic != NULL) { - nativeInputQueue = new MyInputQueue(ic, mainWorkWrite); + nativeInputQueue = new AInputQueue(ic, mainWorkWrite); if (nativeInputQueue->getConsumer().initialize() != android::OK) { delete nativeInputQueue; nativeInputQueue = NULL; @@ -210,8 +354,11 @@ struct NativeCode : public ANativeActivity { String8 externalDataPath; sp nativeWindow; + int32_t lastWindowWidth; + int32_t lastWindowHeight; + jobject inputChannel; - struct MyInputQueue* nativeInputQueue; + struct AInputQueue* nativeInputQueue; // These are used to wake up the main thread to process work. int mainWorkRead; @@ -231,6 +378,18 @@ void android_NativeActivity_setWindowFlags( write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); } +void android_NativeActivity_showSoftInput( + ANativeActivity* activity, int32_t flags) { + NativeCode* code = static_cast(activity); + write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); +} + +void android_NativeActivity_hideSoftInput( + ANativeActivity* activity, int32_t flags) { + NativeCode* code = static_cast(activity); + write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags); +} + // ------------------------------------------------------------------------ /* @@ -246,10 +405,13 @@ static bool mainWorkCallback(int fd, int events, void* data) { if (!read_work(code->mainWorkRead, &work)) { return true; } + + LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); + switch (work.cmd) { case CMD_DEF_KEY: { KeyEvent* keyEvent; - while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) { + while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) { jobject inputEventObj = android_view_KeyEvent_fromNative( code->env, keyEvent); code->env->CallVoidMethod(code->clazz, @@ -269,6 +431,14 @@ static bool mainWorkCallback(int fd, int events, void* data) { code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2); } break; + case CMD_SHOW_SOFT_INPUT: { + code->env->CallVoidMethod(code->clazz, + gNativeActivityClassInfo.showIme, work.arg1); + } break; + case CMD_HIDE_SOFT_INPUT: { + code->env->CallVoidMethod(code->clazz, + gNativeActivityClassInfo.hideIme, work.arg1); + } break; default: LOGW("Unknown work command: %d", work.cmd); break; @@ -283,6 +453,8 @@ static jint loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue, jstring internalDataDir, jstring externalDataDir, int sdkVersion) { + LOG_TRACE("loadNativeCode_native"); + const char* pathStr = env->GetStringUTFChars(path, NULL); NativeCode* code = NULL; @@ -314,6 +486,12 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ } code->mainWorkRead = msgpipe[0]; code->mainWorkWrite = msgpipe[1]; + int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK); + SLOGW_IF(result != 0, "Could not make main work read pipe " + "non-blocking: %s", strerror(errno)); + result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); + SLOGW_IF(result != 0, "Could not make main work write pipe " + "non-blocking: %s", strerror(errno)); code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); code->ANativeActivity::callbacks = &code->callbacks; @@ -346,6 +524,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ static void unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("unloadNativeCode_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; delete code; @@ -355,6 +534,7 @@ unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) static void onStart_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("onStart_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onStart != NULL) { @@ -366,6 +546,7 @@ onStart_native(JNIEnv* env, jobject clazz, jint handle) static void onResume_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("onResume_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onResume != NULL) { @@ -377,6 +558,7 @@ onResume_native(JNIEnv* env, jobject clazz, jint handle) static void onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("onSaveInstanceState_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onSaveInstanceState != NULL) { @@ -389,6 +571,7 @@ onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) static void onPause_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("onPause_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onPause != NULL) { @@ -400,6 +583,7 @@ onPause_native(JNIEnv* env, jobject clazz, jint handle) static void onStop_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("onStop_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onStop != NULL) { @@ -411,6 +595,7 @@ onStop_native(JNIEnv* env, jobject clazz, jint handle) static void onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) { + LOG_TRACE("onLowMemory_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onLowMemory != NULL) { @@ -422,6 +607,7 @@ onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) static void onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused) { + LOG_TRACE("onWindowFocusChanged_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onWindowFocusChanged != NULL) { @@ -433,6 +619,7 @@ onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean fo static void onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) { + LOG_TRACE("onSurfaceCreated_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; code->setSurface(surface); @@ -443,10 +630,17 @@ onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface } } +static int32_t getWindowProp(ANativeWindow* window, int what) { + int value; + int res = window->query(window, what, &value); + return res < 0 ? res : value; +} + static void onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface, jint format, jint width, jint height) { + LOG_TRACE("onSurfaceChanged_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; sp oldNativeWindow = code->nativeWindow; @@ -456,10 +650,41 @@ onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface code->callbacks.onNativeWindowDestroyed(code, oldNativeWindow.get()); } - if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { - code->callbacks.onNativeWindowCreated(code, - code->nativeWindow.get()); + if (code->nativeWindow != NULL) { + if (code->callbacks.onNativeWindowCreated != NULL) { + code->callbacks.onNativeWindowCreated(code, + code->nativeWindow.get()); + } + code->lastWindowWidth = getWindowProp(code->nativeWindow.get(), + NATIVE_WINDOW_WIDTH); + code->lastWindowHeight = getWindowProp(code->nativeWindow.get(), + NATIVE_WINDOW_HEIGHT); } + } else { + // Maybe it resized? + int32_t newWidth = getWindowProp(code->nativeWindow.get(), + NATIVE_WINDOW_WIDTH); + int32_t newHeight = getWindowProp(code->nativeWindow.get(), + NATIVE_WINDOW_HEIGHT); + if (newWidth != code->lastWindowWidth + || newHeight != code->lastWindowHeight) { + if (code->callbacks.onNativeWindowResized != NULL) { + code->callbacks.onNativeWindowResized(code, + code->nativeWindow.get()); + } + } + } + } +} + +static void +onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle) +{ + LOG_TRACE("onSurfaceRedrawNeeded_native"); + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) { + code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get()); } } } @@ -467,6 +692,7 @@ onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface static void onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) { + LOG_TRACE("onSurfaceDestroyed_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { @@ -480,6 +706,7 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa static void onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) { + LOG_TRACE("onInputChannelCreated_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; status_t err = code->setInputChannel(channel); @@ -498,6 +725,7 @@ onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject ch static void onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) { + LOG_TRACE("onInputChannelDestroyed_native"); if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->nativeInputQueue != NULL @@ -509,6 +737,38 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject } } +static void +onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle, + jint x, jint y, jint w, jint h) +{ + LOG_TRACE("onContentRectChanged_native"); + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onContentRectChanged != NULL) { + ARect rect; + rect.left = x; + rect.top = y; + rect.right = x+w; + rect.bottom = y+h; + code->callbacks.onContentRectChanged(code, &rect); + } + } +} + +static void +dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj) +{ + LOG_TRACE("dispatchKeyEvent_native"); + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->nativeInputQueue != NULL) { + KeyEvent* event = new KeyEvent(); + android_view_KeyEvent_toNative(env, eventObj, INPUT_EVENT_NATURE_KEY, event); + code->nativeInputQueue->dispatchEvent(event); + } + } +} + static const JNINativeMethod g_methods[] = { { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;I)I", (void*)loadNativeCode_native }, @@ -522,9 +782,12 @@ static const JNINativeMethod g_methods[] = { { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, + { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native }, { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, + { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native }, + { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native }, }; static const char* const kNativeActivityPathName = "android/app/NativeActivity"; @@ -554,6 +817,12 @@ int register_android_app_NativeActivity(JNIEnv* env) GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, gNativeActivityClassInfo.clazz, "setWindowFormat", "(I)V"); + GET_METHOD_ID(gNativeActivityClassInfo.showIme, + gNativeActivityClassInfo.clazz, + "showIme", "(I)V"); + GET_METHOD_ID(gNativeActivityClassInfo.hideIme, + gNativeActivityClassInfo.clazz, + "hideIme", "(I)V"); return AndroidRuntime::registerNativeMethods( env, kNativeActivityPathName, diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h index f80832811b95b..d7a9a2c3be067 100644 --- a/include/android_runtime/android_app_NativeActivity.h +++ b/include/android_runtime/android_app_NativeActivity.h @@ -17,6 +17,8 @@ #ifndef _ANDROID_APP_NATIVEACTIVITY_H #define _ANDROID_APP_NATIVEACTIVITY_H +#include + #include #include "jni.h" @@ -29,7 +31,65 @@ extern void android_NativeActivity_setWindowFormat( extern void android_NativeActivity_setWindowFlags( ANativeActivity* activity, int32_t values, int32_t mask); +extern void android_NativeActivity_showSoftInput( + ANativeActivity* activity, int32_t flags); + +extern void android_NativeActivity_hideSoftInput( + ANativeActivity* activity, int32_t flags); } // namespace android + +/* + * NDK input queue API. + */ +struct AInputQueue { +public: + /* Creates a consumer associated with an input channel. */ + explicit AInputQueue(const android::sp& channel, int workWrite); + + /* Destroys the consumer and releases its input channel. */ + ~AInputQueue(); + + void attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data); + + void detachLooper(); + + int32_t hasEvents(); + + int32_t getEvent(AInputEvent** outEvent); + + void finishEvent(AInputEvent* event, bool handled); + + + // ---------------------------------------------------------- + + inline android::InputConsumer& getConsumer() { return mConsumer; } + + void dispatchEvent(android::KeyEvent* event); + + android::KeyEvent* consumeUnhandledEvent(); + + int mWorkWrite; + +private: + void doDefaultKey(android::KeyEvent* keyEvent); + + android::InputConsumer mConsumer; + android::PreallocatedInputEventFactory mInputEventFactory; + android::sp mPollLoop; + + int mDispatchKeyRead; + int mDispatchKeyWrite; + + // This is only touched by the event reader thread. It is the current + // key events that came out of the mDispatchingKeys list and are now + //Êdelivered to the app. + android::Vector mDeliveringKeys; + + android::Mutex mLock; + android::Vector mPendingKeys; + android::Vector mDispatchingKeys; +}; + #endif // _ANDROID_APP_NATIVEACTIVITY_H diff --git a/include/ui/Input.h b/include/ui/Input.h index a2e0ba06a2c24..a7d23d48548db 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -43,7 +43,9 @@ enum { /* * Declare a concrete type for the NDK's input event forward declaration. */ -struct AInputEvent { }; +struct AInputEvent { + virtual ~AInputEvent() { } +}; namespace android { diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 11714d536285d..226d1d5a4b20b 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -331,30 +331,4 @@ private: } // namespace android -/* - * NDK input queue API. - */ -struct AInputQueue { -public: - /* Creates a consumer associated with an input channel. */ - explicit AInputQueue(const android::sp& channel); - - /* Destroys the consumer and releases its input channel. */ - virtual ~AInputQueue(); - - inline android::InputConsumer& getConsumer() { return mConsumer; } - - android::status_t consume(android::InputEvent** event); - - void setPollLoop(const android::sp& pollLoop) { mPollLoop = pollLoop; } - const android::sp getPollLoop() const { return mPollLoop; } - - virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0; - -private: - android::InputConsumer mConsumer; - android::PreallocatedInputEventFactory mInputEventFactory; - android::sp mPollLoop; -}; - #endif // _UI_INPUT_TRANSPORT_H diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index 25def3c18ca18..fc83e316dee2d 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -690,22 +690,3 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { } } // namespace android - -// --- AInputQueue --- - -using android::InputEvent; -using android::InputChannel; -using android::InputConsumer; -using android::sp; -using android::status_t; - -AInputQueue::AInputQueue(const sp& channel) : - mConsumer(channel) { -} - -AInputQueue::~AInputQueue() { -} - -status_t AInputQueue::consume(InputEvent** event) { - return mConsumer.consume(&mInputEventFactory, event); -} diff --git a/native/android/input.cpp b/native/android/input.cpp index 89d53e2790571..a4dde510b1f6f 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include using android::InputEvent; @@ -187,65 +189,21 @@ float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_i void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, ALooper_callbackFunc* callback, void* data) { - queue->setPollLoop(static_cast(looper)); - ALooper_addFd(looper, queue->getConsumer().getChannel()->getReceivePipeFd(), - POLLIN, callback, data); + queue->attachLooper(looper, callback, data); } void AInputQueue_detachLooper(AInputQueue* queue) { - queue->getPollLoop()->removeCallback( - queue->getConsumer().getChannel()->getReceivePipeFd()); + queue->detachLooper(); } int AInputQueue_hasEvents(AInputQueue* queue) { - struct pollfd pfd; - - pfd.fd = queue->getConsumer().getChannel()->getReceivePipeFd(); - pfd.events = POLLIN; - pfd.revents = 0; - - int nfd = poll(&pfd, 1, 0); - if (nfd <= 0) return nfd; - return pfd.revents == POLLIN ? 1 : -1; + return queue->hasEvents(); } int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) { - *outEvent = NULL; - - int32_t res = queue->getConsumer().receiveDispatchSignal(); - if (res != android::OK) { - LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", - queue->getConsumer().getChannel()->getName().string(), res); - return -1; - } - - InputEvent* myEvent = NULL; - res = queue->consume(&myEvent); - if (res != android::OK) { - LOGW("channel '%s' ~ Failed to consume input event. status=%d", - queue->getConsumer().getChannel()->getName().string(), res); - queue->getConsumer().sendFinishedSignal(); - return -1; - } - - *outEvent = myEvent; - return 0; + return queue->getEvent(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", - queue->getConsumer().getChannel()->getName().string(), res); - } +void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) { + queue->finishEvent(event, handled != 0); } diff --git a/native/android/native_activity.cpp b/native/android/native_activity.cpp index 509cc3310aa25..0c6823aef1b7d 100644 --- a/native/android/native_activity.cpp +++ b/native/android/native_activity.cpp @@ -29,3 +29,11 @@ void ANativeActivity_setWindowFlags(ANativeActivity* activity, uint32_t addFlags, uint32_t removeFlags) { android_NativeActivity_setWindowFlags(activity, addFlags, addFlags|removeFlags); } + +void ANativeActivity_showSoftInput(ANativeActivity* activity, uint32_t flags) { + android_NativeActivity_showSoftInput(activity, flags); +} + +void ANativeActivity_hideSoftInput(ANativeActivity* activity, uint32_t flags) { + android_NativeActivity_hideSoftInput(activity, flags); +} diff --git a/native/glue/threaded_app/threaded_app.c b/native/glue/threaded_app/threaded_app.c index 2411e93b826e4..452c735236e5f 100644 --- a/native/glue/threaded_app/threaded_app.c +++ b/native/glue/threaded_app/threaded_app.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,19 @@ int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { pthread_cond_broadcast(&android_app->cond); pthread_mutex_unlock(&android_app->mutex); break; + + case APP_CMD_WINDOW_REDRAW_NEEDED: + LOGI("APP_CMD_WINDOW_REDRAW_NEEDED\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->redrawNeeded = 0; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONTENT_RECT_CHANGED: + LOGI("APP_CMD_CONTENT_RECT_CHANGED\n"); + android_app->contentRect = android_app->pendingContentRect; + break; case APP_CMD_DESTROY: LOGI("APP_CMD_DESTROY\n"); @@ -133,6 +147,12 @@ static struct android_app* android_app_create(ANativeActivity* activity) { } android_app->msgread = msgpipe[0]; android_app->msgwrite = msgpipe[1]; + int result = fcntl(android_app->msgread, F_SETFL, O_NONBLOCK); + if (result != 0) LOGW("Could not make message read pipe " + "non-blocking: %s", strerror(errno)); + result = fcntl(android_app->msgwrite, F_SETFL, O_NONBLOCK); + if (result != 0) LOGW("Could not make message write pipe " + "non-blocking: %s", strerror(errno)); pthread_attr_t attr; pthread_attr_init(&attr); @@ -184,6 +204,23 @@ static void android_app_set_activity_state(struct android_app* android_app, int8 pthread_mutex_unlock(&android_app->mutex); } +static void android_app_wait_redraw(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app->redrawNeeded = 1; + android_app_write_cmd(android_app, APP_CMD_WINDOW_REDRAW_NEEDED); + while (android_app->redrawNeeded) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_content_rect(struct android_app* android_app, const ARect* rect) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingContentRect = *rect; + android_app_write_cmd(android_app, APP_CMD_CONTENT_RECT_CHANGED); + pthread_mutex_unlock(&android_app->mutex); +} + static void android_app_free(struct android_app* android_app) { pthread_mutex_lock(&android_app->mutex); android_app_write_cmd(android_app, APP_CMD_DESTROY); @@ -231,6 +268,8 @@ static void onStop(ANativeActivity* activity) { static void onLowMemory(ANativeActivity* activity) { LOGI("LowMemory: %p\n", activity); + android_app_write_cmd((struct android_app*)activity->instance, + APP_CMD_LOW_MEMORY); } static void onWindowFocusChanged(ANativeActivity* activity, int focused) { @@ -244,6 +283,23 @@ static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* wind android_app_set_window((struct android_app*)activity->instance, window); } +static void onNativeWindowResized(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowResized: %p -- %p\n", activity, window); + android_app_write_cmd((struct android_app*)activity->instance, + APP_CMD_WINDOW_RESIZED); +} + +static void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowRedrawNeeded: %p -- %p\n", activity, window); + android_app_wait_redraw((struct android_app*)activity->instance); +} + +static void onContentRectChanged(ANativeActivity* activity, const ARect* rect) { + LOGI("ContentRectChanged: %p -- (%d,%d)-(%d,%d)\n", activity, rect->left, + rect->top, rect->right, rect->bottom); + android_app_set_content_rect((struct android_app*)activity->instance, rect); +} + static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); android_app_set_window((struct android_app*)activity->instance, NULL); @@ -268,12 +324,15 @@ void ANativeActivity_onCreate(ANativeActivity* activity, activity->callbacks->onSaveInstanceState = onSaveInstanceState; activity->callbacks->onPause = onPause; activity->callbacks->onStop = onStop; - activity->callbacks->onLowMemory = onLowMemory; activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowResized = onNativeWindowResized; + activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded; activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; activity->callbacks->onInputQueueCreated = onInputQueueCreated; activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + activity->callbacks->onContentRectChanged = onContentRectChanged; + activity->callbacks->onLowMemory = onLowMemory; activity->instance = android_app_create(activity); } diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h index d0ff05277330b..ea6f05fbced88 100644 --- a/native/include/android/native_activity.h +++ b/native/include/android/native_activity.h @@ -146,6 +146,21 @@ typedef struct ANativeActivityCallbacks { */ void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window); + /** + * The drawing window for this native activity has been resized. You should + * retrieve the new size from the window and ensure that your rendering in + * it now matches. + */ + void (*onNativeWindowResized)(ANativeActivity* activity, ANativeWindow* window); + + /** + * The drawing window for this native activity needs to be redrawn. To avoid + * transient artifacts during screen changes (such resizing after rotation), + * applications should not return from this function until they have finished + * drawing their window in its current state. + */ + void (*onNativeWindowRedrawNeeded)(ANativeActivity* activity, ANativeWindow* window); + /** * The drawing window for this native activity is going to be destroyed. * You MUST ensure that you do not touch the window object after returning @@ -169,6 +184,11 @@ typedef struct ANativeActivityCallbacks { */ void (*onInputQueueDestroyed)(ANativeActivity* activity, AInputQueue* queue); + /** + * The rectangle in the window in which content should be placed has changed. + */ + void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect); + /** * The system is running low on memory. Use this callback to release * resources you do not need, to help the system avoid killing more @@ -197,6 +217,28 @@ void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format); void ANativeActivity_setWindowFlags(ANativeActivity* activity, uint32_t addFlags, uint32_t removeFlags); +/** + * Flags for ANativeActivity_showSoftInput; see the Java InputMethodManager + * API for documentation. + */ +enum { + ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = 0x0001, + ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED = 0x0002, +}; + +void ANativeActivity_showSoftInput(ANativeActivity* activity, uint32_t flags); + +/** + * Flags for ANativeActivity_hideSoftInput; see the Java InputMethodManager + * API for documentation. + */ +enum { + ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = 0x0001, + ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = 0x0002, +}; + +void ANativeActivity_hideSoftInput(ANativeActivity* activity, uint32_t flags); + #ifdef __cplusplus }; #endif diff --git a/native/include/android_glue/threaded_app.h b/native/include/android_glue/threaded_app.h index adfdbea06aebe..2b58e9c2d1f8c 100644 --- a/native/include/android_glue/threaded_app.h +++ b/native/include/android_glue/threaded_app.h @@ -48,6 +48,10 @@ struct android_app { // When non-NULL, this is the window surface that the app can draw in. ANativeWindow* window; + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + // Current state of the app's activity. May be either APP_CMD_START, // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. int activityState; @@ -69,8 +73,10 @@ struct android_app { int running; int destroyed; + int redrawNeeded; AInputQueue* pendingInputQueue; ANativeWindow* pendingWindow; + ARect pendingContentRect; }; enum { @@ -104,6 +110,26 @@ enum { */ APP_CMD_WINDOW_CHANGED, + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + /** * Command from main thread: the app's activity window has gained * input focus. @@ -116,6 +142,12 @@ enum { */ APP_CMD_LOST_FOCUS, + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + /** * Command from main thread: the app's activity has been started. */ diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 56425887a418f..b9232c8b0f351 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -104,7 +104,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // mDecor itself, or a child of mDecor where the contents go. private ViewGroup mContentParent; - SurfaceHolder.Callback mTakeSurfaceCallback; + SurfaceHolder.Callback2 mTakeSurfaceCallback; BaseSurfaceHolder mSurfaceHolder; InputQueue.Callback mTakeInputQueueCallback; @@ -248,7 +248,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void takeSurface(SurfaceHolder.Callback callback) { + public void takeSurface(SurfaceHolder.Callback2 callback) { mTakeSurfaceCallback = callback; } @@ -2038,7 +2038,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - public android.view.SurfaceHolder.Callback willYouTakeTheSurface() { + public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() { return mFeatureId < 0 ? mTakeSurfaceCallback : null; }