diff --git a/api/current.xml b/api/current.xml
index d32a10809dc81..9e626373c84d2 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26353,7 +26353,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;
}