diff options
-rw-r--r-- | api/current.xml | 66 | ||||
-rw-r--r-- | core/java/android/app/NativeActivity.java | 101 | ||||
-rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 47 | ||||
-rw-r--r-- | core/java/android/view/SurfaceHolder.java | 17 | ||||
-rw-r--r-- | core/java/android/view/SurfaceView.java | 37 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 14 | ||||
-rw-r--r-- | core/java/android/view/Window.java | 2 | ||||
-rw-r--r-- | core/java/com/android/internal/view/RootViewSurfaceTaker.java | 2 | ||||
-rw-r--r-- | core/jni/android_app_NativeActivity.cpp | 337 | ||||
-rw-r--r-- | include/android_runtime/android_app_NativeActivity.h | 60 | ||||
-rw-r--r-- | include/ui/Input.h | 4 | ||||
-rw-r--r-- | include/ui/InputTransport.h | 26 | ||||
-rw-r--r-- | libs/ui/InputTransport.cpp | 19 | ||||
-rw-r--r-- | native/android/input.cpp | 62 | ||||
-rw-r--r-- | native/android/native_activity.cpp | 8 | ||||
-rw-r--r-- | native/glue/threaded_app/threaded_app.c | 61 | ||||
-rw-r--r-- | native/include/android/native_activity.h | 42 | ||||
-rw-r--r-- | native/include/android_glue/threaded_app.h | 32 | ||||
-rw-r--r-- | policy/src/com/android/internal/policy/impl/PhoneWindow.java | 6 |
19 files changed, 768 insertions, 175 deletions
diff --git a/api/current.xml b/api/current.xml index 086a87a..9486ca6 100644 --- a/api/current.xml +++ b/api/current.xml @@ -28906,7 +28906,9 @@ > <implements name="android.view.InputQueue.Callback"> </implements> -<implements name="android.view.SurfaceHolder.Callback"> +<implements name="android.view.SurfaceHolder.Callback2"> +</implements> +<implements name="android.view.ViewTreeObserver.OnGlobalLayoutListener"> </implements> <constructor name="NativeActivity" type="android.app.NativeActivity" @@ -28916,6 +28918,17 @@ visibility="public" > </constructor> +<method name="onGlobalLayout" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="onInputQueueCreated" return="void" abstract="false" @@ -28987,6 +29000,19 @@ <parameter name="holder" type="android.view.SurfaceHolder"> </parameter> </method> +<method name="surfaceRedrawNeeded" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="holder" type="android.view.SurfaceHolder"> +</parameter> +</method> <field name="META_DATA_LIB_NAME" type="java.lang.String" transient="false" @@ -147148,6 +147174,19 @@ <parameter name="holder" type="android.view.SurfaceHolder"> </parameter> </method> +<method name="onSurfaceRedrawNeeded" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="holder" type="android.view.SurfaceHolder"> +</parameter> +</method> <method name="onTouchEvent" return="void" abstract="false" @@ -183229,6 +183268,29 @@ </parameter> </method> </interface> +<interface name="SurfaceHolder.Callback2" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.view.SurfaceHolder.Callback"> +</implements> +<method name="surfaceRedrawNeeded" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="holder" type="android.view.SurfaceHolder"> +</parameter> +</method> +</interface> <class name="SurfaceView" extends="android.view.View" abstract="false" @@ -191868,7 +191930,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="callback" type="android.view.SurfaceHolder.Callback"> +<parameter name="callback" type="android.view.SurfaceHolder.Callback2"> </parameter> </method> <method name="togglePanel" diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index d72dda7..ccc9ae3 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -2,6 +2,7 @@ package android.app; import dalvik.system.PathClassLoader; +import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -11,12 +12,16 @@ import android.os.Bundle; import android.os.Environment; import android.os.Looper; import android.os.MessageQueue; +import android.util.AttributeSet; import android.view.InputChannel; import android.view.InputQueue; import android.view.KeyEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; +import android.view.WindowManager; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.view.inputmethod.InputMethodManager; import java.io.File; @@ -24,15 +29,26 @@ import java.io.File; * Convenience for implementing an activity that will be implemented * purely in native code. That is, a game (or game-like thing). */ -public class NativeActivity extends Activity implements SurfaceHolder.Callback, - InputQueue.Callback { +public class NativeActivity extends Activity implements SurfaceHolder.Callback2, + InputQueue.Callback, OnGlobalLayoutListener { public static final String META_DATA_LIB_NAME = "android.app.lib_name"; + private NativeContentView mNativeContentView; + private InputMethodManager mIMM; + private int mNativeHandle; private InputQueue mCurInputQueue; private SurfaceHolder mCurSurfaceHolder; + final int[] mLocation = new int[2]; + int mLastContentX; + int mLastContentY; + int mLastContentWidth; + int mLastContentHeight; + + private boolean mDispatchingUnhandledKey; + private boolean mDestroyed; private native int loadNativeCode(String path, MessageQueue queue, @@ -49,18 +65,44 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, private native void onSurfaceCreatedNative(int handle, Surface surface); private native void onSurfaceChangedNative(int handle, Surface surface, int format, int width, int height); + private native void onSurfaceRedrawNeededNative(int handle, Surface surface); private native void onSurfaceDestroyedNative(int handle); private native void onInputChannelCreatedNative(int handle, InputChannel channel); private native void onInputChannelDestroyedNative(int handle, InputChannel channel); + private native void onContentRectChangedNative(int handle, int x, int y, int w, int h); + private native void dispatchKeyEventNative(int handle, KeyEvent event); + + static class NativeContentView extends View { + NativeActivity mActivity; + + public NativeContentView(Context context) { + super(context); + } + + public NativeContentView(Context context, AttributeSet attrs) { + super(context, attrs); + } + } @Override protected void onCreate(Bundle savedInstanceState) { String libname = "main"; ActivityInfo ai; + mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + getWindow().takeSurface(this); getWindow().takeInputQueue(this); getWindow().setFormat(PixelFormat.RGB_565); + getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED + | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + mNativeContentView = new NativeContentView(this); + mNativeContentView.mActivity = this; + setContentView(mNativeContentView); + mNativeContentView.requestFocus(); + mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); try { ai = getPackageManager().getActivityInfo( @@ -165,6 +207,18 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, } } + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (mDispatchingUnhandledKey) { + return super.dispatchKeyEvent(event); + } else { + // Key events from the IME do not go through the input channel; + // we need to intercept them here to hand to the application. + dispatchKeyEventNative(mNativeHandle, event); + return true; + } + } + public void surfaceCreated(SurfaceHolder holder) { if (!mDestroyed) { mCurSurfaceHolder = holder; @@ -179,6 +233,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, } } + public void surfaceRedrawNeeded(SurfaceHolder holder) { + if (!mDestroyed) { + mCurSurfaceHolder = holder; + onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); + } + } + public void surfaceDestroyed(SurfaceHolder holder) { mCurSurfaceHolder = null; if (!mDestroyed) { @@ -200,10 +261,32 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, } } + public void onGlobalLayout() { + mNativeContentView.getLocationInWindow(mLocation); + int w = mNativeContentView.getWidth(); + int h = mNativeContentView.getHeight(); + if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY + || w != mLastContentWidth || h != mLastContentHeight) { + mLastContentX = mLocation[0]; + mLastContentY = mLocation[1]; + mLastContentWidth = w; + mLastContentHeight = h; + if (!mDestroyed) { + onContentRectChangedNative(mNativeHandle, mLastContentX, + mLastContentY, mLastContentWidth, mLastContentHeight); + } + } + } + void dispatchUnhandledKeyEvent(KeyEvent event) { - View decor = getWindow().getDecorView(); - if (decor != null) { - decor.dispatchKeyEvent(event); + try { + mDispatchingUnhandledKey = true; + View decor = getWindow().getDecorView(); + if (decor != null) { + decor.dispatchKeyEvent(event); + } + } finally { + mDispatchingUnhandledKey = false; } } @@ -214,4 +297,12 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, void setWindowFormat(int format) { getWindow().setFormat(format); } + + void showIme(int mode) { + mIMM.showSoftInput(mNativeContentView, mode); + } + + void hideIme(int mode) { + mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); + } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 6f12f19..2d120e8 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -336,7 +336,7 @@ public abstract class WallpaperService extends Service { ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); if (mCreated) { - updateSurface(false, false); + updateSurface(false, false, false); } } @@ -421,6 +421,13 @@ public abstract class WallpaperService extends Service { } /** + * Convenience for {@link SurfaceHolder.Callback#surfaceRedrawNeeded + * SurfaceHolder.Callback.surfaceRedrawNeeded()}. + */ + public void onSurfaceRedrawNeeded(SurfaceHolder holder) { + } + + /** * Convenience for {@link SurfaceHolder.Callback#surfaceCreated * SurfaceHolder.Callback.surfaceCreated()}. */ @@ -450,7 +457,7 @@ public abstract class WallpaperService extends Service { } } - void updateSurface(boolean forceRelayout, boolean forceReport) { + void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { if (mDestroyed) { Log.w(TAG, "Ignoring updateSurface: destroyed"); } @@ -467,7 +474,7 @@ public abstract class WallpaperService extends Service { final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); final boolean flagsChanged = mCurWindowFlags != mWindowFlags; if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged - || typeChanged || flagsChanged) { + || typeChanged || flagsChanged || redrawNeeded) { if (DEBUG) Log.v(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged); @@ -555,6 +562,10 @@ public abstract class WallpaperService extends Service { } } } + + redrawNeeded |= creating + || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0; + if (forceReport || creating || surfaceCreating || formatChanged || sizeChanged) { if (DEBUG) { @@ -578,10 +589,24 @@ public abstract class WallpaperService extends Service { } } } + + if (redrawNeeded) { + onSurfaceRedrawNeeded(mSurfaceHolder); + SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); + if (callbacks != null) { + for (SurfaceHolder.Callback c : callbacks) { + if (c instanceof SurfaceHolder.Callback2) { + ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( + mSurfaceHolder); + } + } + } + } + } finally { mIsCreating = false; mSurfaceCreated = true; - if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { + if (redrawNeeded) { mSession.finishDrawing(mWindow); } } @@ -618,7 +643,7 @@ public abstract class WallpaperService extends Service { onCreate(mSurfaceHolder); mInitializing = false; - updateSurface(false, false); + updateSurface(false, false, false); } void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { @@ -647,7 +672,7 @@ public abstract class WallpaperService extends Service { // If becoming visible, in preview mode the surface // may have been destroyed so now we need to make // sure it is re-created. - updateSurface(false, false); + updateSurface(false, false, false); } onVisibilityChanged(visible); } @@ -852,7 +877,7 @@ public abstract class WallpaperService extends Service { return; } case MSG_UPDATE_SURFACE: - mEngine.updateSurface(true, false); + mEngine.updateSurface(true, false, false); break; case MSG_VISIBILITY_CHANGED: if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine @@ -868,14 +893,8 @@ public abstract class WallpaperService extends Service { } break; case MSG_WINDOW_RESIZED: { final boolean reportDraw = message.arg1 != 0; - mEngine.updateSurface(true, false); + mEngine.updateSurface(true, false, reportDraw); mEngine.doOffsetsChanged(); - if (reportDraw) { - try { - mEngine.mSession.finishDrawing(mEngine.mWindow); - } catch (RemoteException e) { - } - } } break; case MSG_TOUCH_EVENT: { MotionEvent ev = (MotionEvent)message.obj; diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java index 34e4638..0d38f7b 100644 --- a/core/java/android/view/SurfaceHolder.java +++ b/core/java/android/view/SurfaceHolder.java @@ -119,6 +119,23 @@ public interface SurfaceHolder { } /** + * Additional callbacks that can be received for {@link Callback}. + */ + public interface Callback2 extends Callback { + /** + * Called when the application needs to redraw the content of its + * surface, after it is resized or for some other reason. By not + * returning here until the redraw is complete, you can ensure that + * the user will not see your surface in a bad state (at its new + * size before it has been correctly drawn that way). This will + * typically be preceeded by a call to {@link #surfaceChanged}. + * + * @param holder The SurfaceHolder whose surface has changed. + */ + public void surfaceRedrawNeeded(SurfaceHolder holder); + } + + /** * Add a Callback interface for this holder. There can several Callback * interfaces associated to a holder. * diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index e4d1ae1..c469bcc 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -123,7 +123,7 @@ public class SurfaceView extends View { handleGetNewSurface(); } break; case UPDATE_WINDOW_MSG: { - updateWindow(false); + updateWindow(false, false); } break; } } @@ -132,7 +132,7 @@ public class SurfaceView extends View { final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() { public void onScrollChanged() { - updateWindow(false); + updateWindow(false, false); } }; @@ -210,7 +210,7 @@ public class SurfaceView extends View { super.onWindowVisibilityChanged(visibility); mWindowVisibility = visibility == VISIBLE; mRequestedVisible = mWindowVisibility && mViewVisibility; - updateWindow(false); + updateWindow(false, false); } @Override @@ -218,7 +218,7 @@ public class SurfaceView extends View { super.setVisibility(visibility); mViewVisibility = visibility == VISIBLE; mRequestedVisible = mWindowVisibility && mViewVisibility; - updateWindow(false); + updateWindow(false, false); } /** @@ -232,7 +232,7 @@ public class SurfaceView extends View { */ protected void showSurface() { if (mSession != null) { - updateWindow(true); + updateWindow(true, false); } } @@ -265,7 +265,7 @@ public class SurfaceView extends View { protected void onDetachedFromWindow() { getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener); mRequestedVisible = false; - updateWindow(false); + updateWindow(false, false); mHaveFrame = false; if (mWindow != null) { try { @@ -290,7 +290,7 @@ public class SurfaceView extends View { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - updateWindow(false); + updateWindow(false, false); } @Override @@ -343,7 +343,7 @@ public class SurfaceView extends View { } // reposition ourselves where the surface is mHaveFrame = true; - updateWindow(false); + updateWindow(false, false); super.dispatchDraw(canvas); } @@ -397,7 +397,7 @@ public class SurfaceView extends View { mWindowType = type; } - private void updateWindow(boolean force) { + private void updateWindow(boolean force, boolean redrawNeeded) { if (!mHaveFrame) { return; } @@ -425,7 +425,7 @@ public class SurfaceView extends View { final boolean typeChanged = mType != mRequestedType; if (force || creating || formatChanged || sizeChanged || visibleChanged || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1] - || mUpdateWindowNeeded || mReportDrawNeeded) { + || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { if (localLOGV) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged @@ -524,6 +524,8 @@ public class SurfaceView extends View { } try { + redrawNeeded |= creating | reportDrawNeeded; + if (visible) { mDestroyReportNeeded = true; @@ -541,8 +543,13 @@ public class SurfaceView extends View { } if (creating || formatChanged || sizeChanged || visibleChanged || realSizeChanged) { + } + if (redrawNeeded) { for (SurfaceHolder.Callback c : callbacks) { - c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); + if (c instanceof SurfaceHolder.Callback2) { + ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( + mSurfaceHolder); + } } } } else { @@ -550,7 +557,7 @@ public class SurfaceView extends View { } } finally { mIsCreating = false; - if (creating || reportDrawNeeded) { + if (redrawNeeded) { mSession.finishDrawing(mWindow); } } @@ -580,7 +587,7 @@ public class SurfaceView extends View { void handleGetNewSurface() { mNewSurfaceNeeded = true; - updateWindow(false); + updateWindow(false, false); } /** @@ -696,7 +703,7 @@ public class SurfaceView extends View { mRequestedFormat = format; if (mWindow != null) { - updateWindow(false); + updateWindow(false, false); } } @@ -713,7 +720,7 @@ public class SurfaceView extends View { case SURFACE_TYPE_PUSH_BUFFERS: mRequestedType = type; if (mWindow != null) { - updateWindow(false); + updateWindow(false, false); } break; } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 329226e..a89e7f6 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -130,7 +130,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn int mViewVisibility; boolean mAppVisible = true; - SurfaceHolder.Callback mSurfaceHolderCallback; + SurfaceHolder.Callback2 mSurfaceHolderCallback; BaseSurfaceHolder mSurfaceHolder; boolean mIsCreating; boolean mDrawingAllowed; @@ -1152,6 +1152,18 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn Log.v("ViewRoot", "FINISHED DRAWING: " + mWindowAttributes.getTitle()); } mReportNextDraw = false; + if (mSurfaceHolder != null && mSurface.isValid()) { + mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder); + SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); + if (callbacks != null) { + for (SurfaceHolder.Callback c : callbacks) { + if (c instanceof SurfaceHolder.Callback2) { + ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( + mSurfaceHolder); + } + } + } + } try { sWindowSession.finishDrawing(mWindow); } catch (RemoteException e) { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 9b31b9c..be681cc 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -485,7 +485,7 @@ public abstract class Window { * to operate (such as for receiving input events). The given SurfaceHolder * callback will be used to tell you about state changes to the surface. */ - public abstract void takeSurface(SurfaceHolder.Callback callback); + public abstract void takeSurface(SurfaceHolder.Callback2 callback); /** * Take ownership of this window's InputQueue. The window will no diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java index 7ff8d4c..9c1b558 100644 --- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java +++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java @@ -5,7 +5,7 @@ import android.view.SurfaceHolder; /** hahahah */ public interface RootViewSurfaceTaker { - SurfaceHolder.Callback willYouTakeTheSurface(); + SurfaceHolder.Callback2 willYouTakeTheSurface(); void setSurfaceType(int type); void setSurfaceFormat(int format); void setSurfaceKeepScreenOn(boolean keepOn); diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 54a9c2a..acbf854 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -19,6 +19,7 @@ #include <poll.h> #include <dlfcn.h> +#include <fcntl.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> @@ -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<android::InputChannel>& channel, int workWrite) - : AInputQueue(channel), mWorkWrite(workWrite) { +} // namespace android + +using namespace android; + +AInputQueue::AInputQueue(const sp<InputChannel>& 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<android::PollLoop*>(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); + if (mDispatchingKeys.size() > 0) { + KeyEvent* kevent = mDispatchingKeys[0]; + *outEvent = kevent; + mDispatchingKeys.removeAt(0); + mDeliveringKeys.add(kevent); } - mPendingKeys.add(keyEvent); mLock.unlock(); + if (*outEvent != NULL) { + return 0; + } } - KeyEvent* getNextEvent() { - KeyEvent* event = NULL; - - mLock.lock(); - if (mPendingKeys.size() > 0) { - event = mPendingKeys[0]; - mPendingKeys.removeAt(0); + 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<N; i++) { + if (mDeliveringKeys[i] == event) { + delete event; + mDeliveringKeys.removeAt(i); + needFinished = false; + break; } - mLock.unlock(); - - return event; } - int mWorkWrite; + 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(); - Mutex mLock; - Vector<KeyEvent*> mPendingKeys; -}; +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<InputChannel> 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<ANativeWindow> 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<NativeCode*>(activity); + write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); +} + +void android_NativeActivity_hideSoftInput( + ANativeActivity* activity, int32_t flags) { + NativeCode* code = static_cast<NativeCode*>(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<ANativeWindow> oldNativeWindow = code->nativeWindow; @@ -456,17 +650,49 @@ 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()); + } + } +} + +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 f808328..d7a9a2c 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 <ui/InputTransport.h> + #include <android/native_activity.h> #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<android::InputChannel>& 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<android::PollLoop> 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<android::KeyEvent*> mDeliveringKeys; + + android::Mutex mLock; + android::Vector<android::KeyEvent*> mPendingKeys; + android::Vector<android::KeyEvent*> mDispatchingKeys; +}; + #endif // _ANDROID_APP_NATIVEACTIVITY_H diff --git a/include/ui/Input.h b/include/ui/Input.h index a2e0ba06..a7d23d4 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 11714d5..226d1d5 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<android::InputChannel>& 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<android::PollLoop>& pollLoop) { mPollLoop = pollLoop; } - const android::sp<android::PollLoop> getPollLoop() const { return mPollLoop; } - - virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0; - -private: - android::InputConsumer mConsumer; - android::PreallocatedInputEventFactory mInputEventFactory; - android::sp<android::PollLoop> mPollLoop; -}; - #endif // _UI_INPUT_TRANSPORT_H diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index 25def3c..fc83e31 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<InputChannel>& 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 89d53e2..a4dde51 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -22,6 +22,8 @@ #include <ui/InputTransport.h> #include <utils/PollLoop.h> +#include <android_runtime/android_app_NativeActivity.h> + #include <poll.h> 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<android::PollLoop*>(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; -} - -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); - } + return queue->getEvent(outEvent); +} + +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 509cc33..0c6823a 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 2411e93..452c735 100644 --- a/native/glue/threaded_app/threaded_app.c +++ b/native/glue/threaded_app/threaded_app.c @@ -18,6 +18,7 @@ #include <jni.h> #include <errno.h> +#include <fcntl.h> #include <string.h> #include <unistd.h> #include <sys/resource.h> @@ -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 d0ff052..ea6f05f 100644 --- a/native/include/android/native_activity.h +++ b/native/include/android/native_activity.h @@ -147,6 +147,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 * from this function: in the common case of drawing to the window from @@ -170,6 +185,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 * important processes. @@ -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 adfdbea..2b58e9c 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 { @@ -105,6 +111,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. */ @@ -117,6 +143,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. */ APP_CMD_START, diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 879679f..8321473 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -109,7 +109,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; @@ -255,7 +255,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void takeSurface(SurfaceHolder.Callback callback) { + public void takeSurface(SurfaceHolder.Callback2 callback) { mTakeSurfaceCallback = callback; } @@ -2085,7 +2085,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; } |