IME events are now dispatched to native applications.
And also: - APIs to show and hide the IME, and control its interaction with the app. - APIs to tell the app when its window resizes and needs to be redrawn. - API to tell the app the content rectangle of its window (to layout around the IME or status bar). There is still a problem with IME interaction -- we need a way for the app to deliver events to the IME before it handles them, so that for example the back key will close the IME instead of finishing the app. Change-Id: I37b75fc2ec533750ef36ca3aedd2f0cc0b5813cd
This commit is contained in:
@ -26353,7 +26353,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"
|
||||
@ -26363,6 +26365,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"
|
||||
@ -26434,6 +26447,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"
|
||||
@ -142938,6 +142964,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"
|
||||
@ -179068,6 +179107,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"
|
||||
@ -187657,7 +187719,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"
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,6 +420,13 @@ public abstract class WallpaperService extends Service {
|
||||
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
@ -118,6 +118,23 @@ public interface SurfaceHolder {
|
||||
public void surfaceDestroyed(SurfaceHolder holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
int mViewVisibility;
|
||||
boolean mAppVisible = true;
|
||||
|
||||
SurfaceHolder.Callback mSurfaceHolderCallback;
|
||||
SurfaceHolder.Callback2 mSurfaceHolderCallback;
|
||||
BaseSurfaceHolder mSurfaceHolder;
|
||||
boolean mIsCreating;
|
||||
boolean mDrawingAllowed;
|
||||
@ -1263,6 +1263,18 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
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) {
|
||||
|
@ -478,7 +478,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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
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<N; i++) {
|
||||
if (mDeliveringKeys[i] == event) {
|
||||
delete event;
|
||||
mDeliveringKeys.removeAt(i);
|
||||
needFinished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Mutex mLock;
|
||||
Vector<KeyEvent*> 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<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,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,
|
||||
|
@ -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
|
||||
//<2F>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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user