NativeActivity.java revision 6798b62e9230b744d877c4fcab2a0d949eab9f8f
1package android.app; 2 3import com.android.internal.view.IInputMethodCallback; 4import com.android.internal.view.IInputMethodSession; 5 6import android.content.Context; 7import android.content.pm.ActivityInfo; 8import android.content.pm.ApplicationInfo; 9import android.content.pm.PackageManager; 10import android.content.res.AssetManager; 11import android.content.res.Configuration; 12import android.graphics.PixelFormat; 13import android.os.Build; 14import android.os.Bundle; 15import android.os.Environment; 16import android.os.Looper; 17import android.os.MessageQueue; 18import android.util.AttributeSet; 19import android.view.InputChannel; 20import android.view.InputQueue; 21import android.view.KeyEvent; 22import android.view.Surface; 23import android.view.SurfaceHolder; 24import android.view.View; 25import android.view.WindowManager; 26import android.view.ViewTreeObserver.OnGlobalLayoutListener; 27import android.view.inputmethod.InputMethodManager; 28 29import java.io.File; 30import java.lang.ref.WeakReference; 31 32/** 33 * Convenience for implementing an activity that will be implemented 34 * purely in native code. That is, a game (or game-like thing). There 35 * is no need to derive from this class; you can simply declare it in your 36 * manifest, and use the NDK APIs from there. 37 * 38 * <p>A typical manifest would look like: 39 * 40 * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml 41 * manifest} 42 * 43 * <p>A very simple example of native code that is run by NativeActivity 44 * follows. This reads input events from the user and uses OpenGLES to 45 * draw into the native activity's window. 46 * 47 * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all} 48 */ 49public class NativeActivity extends Activity implements SurfaceHolder.Callback2, 50 InputQueue.Callback, OnGlobalLayoutListener { 51 public static final String META_DATA_LIB_NAME = "android.app.lib_name"; 52 53 public static final String KEY_NATIVE_SAVED_STATE = "android:native_state"; 54 55 private NativeContentView mNativeContentView; 56 private InputMethodManager mIMM; 57 private InputMethodCallback mInputMethodCallback; 58 59 private int mNativeHandle; 60 61 private InputQueue mCurInputQueue; 62 private SurfaceHolder mCurSurfaceHolder; 63 64 final int[] mLocation = new int[2]; 65 int mLastContentX; 66 int mLastContentY; 67 int mLastContentWidth; 68 int mLastContentHeight; 69 70 private boolean mDispatchingUnhandledKey; 71 72 private boolean mDestroyed; 73 74 private native int loadNativeCode(String path, MessageQueue queue, 75 String internalDataPath, String externalDataPath, int sdkVersion, 76 AssetManager assetMgr, byte[] savedState); 77 private native void unloadNativeCode(int handle); 78 79 private native void onStartNative(int handle); 80 private native void onResumeNative(int handle); 81 private native byte[] onSaveInstanceStateNative(int handle); 82 private native void onPauseNative(int handle); 83 private native void onStopNative(int handle); 84 private native void onConfigurationChangedNative(int handle); 85 private native void onLowMemoryNative(int handle); 86 private native void onWindowFocusChangedNative(int handle, boolean focused); 87 private native void onSurfaceCreatedNative(int handle, Surface surface); 88 private native void onSurfaceChangedNative(int handle, Surface surface, 89 int format, int width, int height); 90 private native void onSurfaceRedrawNeededNative(int handle, Surface surface); 91 private native void onSurfaceDestroyedNative(int handle); 92 private native void onInputChannelCreatedNative(int handle, InputChannel channel); 93 private native void onInputChannelDestroyedNative(int handle, InputChannel channel); 94 private native void onContentRectChangedNative(int handle, int x, int y, int w, int h); 95 private native void dispatchKeyEventNative(int handle, KeyEvent event); 96 private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled); 97 98 static class NativeContentView extends View { 99 NativeActivity mActivity; 100 101 public NativeContentView(Context context) { 102 super(context); 103 } 104 105 public NativeContentView(Context context, AttributeSet attrs) { 106 super(context, attrs); 107 } 108 } 109 110 static class InputMethodCallback extends IInputMethodCallback.Stub { 111 WeakReference<NativeActivity> mNa; 112 113 InputMethodCallback(NativeActivity na) { 114 mNa = new WeakReference<NativeActivity>(na); 115 } 116 117 @Override 118 public void finishedEvent(int seq, boolean handled) { 119 NativeActivity na = mNa.get(); 120 if (na != null) { 121 na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled); 122 } 123 } 124 125 @Override 126 public void sessionCreated(IInputMethodSession session) { 127 // Stub -- not for use in the client. 128 } 129 } 130 131 @Override 132 protected void onCreate(Bundle savedInstanceState) { 133 String libname = "main"; 134 ActivityInfo ai; 135 136 mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 137 mInputMethodCallback = new InputMethodCallback(this); 138 139 getWindow().takeSurface(this); 140 getWindow().takeInputQueue(this); 141 getWindow().setFormat(PixelFormat.RGB_565); 142 getWindow().setSoftInputMode( 143 WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED 144 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 145 146 mNativeContentView = new NativeContentView(this); 147 mNativeContentView.mActivity = this; 148 setContentView(mNativeContentView); 149 mNativeContentView.requestFocus(); 150 mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); 151 152 try { 153 ai = getPackageManager().getActivityInfo( 154 getIntent().getComponent(), PackageManager.GET_META_DATA); 155 if (ai.metaData != null) { 156 String ln = ai.metaData.getString(META_DATA_LIB_NAME); 157 if (ln != null) libname = ln; 158 } 159 } catch (PackageManager.NameNotFoundException e) { 160 throw new RuntimeException("Error getting activity info", e); 161 } 162 163 String path = null; 164 165 if ((ai.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) == 0) { 166 // If the application does not have (Java) code, then no ClassLoader 167 // has been set up for it. We will need to do our own search for 168 // the native code. 169 File libraryFile = new File(ai.applicationInfo.nativeLibraryDir, 170 System.mapLibraryName(libname)); 171 if (libraryFile.exists()) { 172 path = libraryFile.getPath(); 173 } 174 } 175 176 if (path == null) { 177 throw new IllegalArgumentException("Unable to find native library: " + libname); 178 } 179 180 byte[] nativeSavedState = savedInstanceState != null 181 ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; 182 183 mNativeHandle = loadNativeCode(path, Looper.myQueue(), 184 getFilesDir().toString(), 185 Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(), 186 Build.VERSION.SDK_INT, getAssets(), nativeSavedState); 187 188 if (mNativeHandle == 0) { 189 throw new IllegalArgumentException("Unable to load native library: " + path); 190 } 191 super.onCreate(savedInstanceState); 192 } 193 194 @Override 195 protected void onDestroy() { 196 mDestroyed = true; 197 if (mCurSurfaceHolder != null) { 198 onSurfaceDestroyedNative(mNativeHandle); 199 mCurSurfaceHolder = null; 200 } 201 if (mCurInputQueue != null) { 202 onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel()); 203 mCurInputQueue = null; 204 } 205 unloadNativeCode(mNativeHandle); 206 super.onDestroy(); 207 } 208 209 @Override 210 protected void onPause() { 211 super.onPause(); 212 onPauseNative(mNativeHandle); 213 } 214 215 @Override 216 protected void onResume() { 217 super.onResume(); 218 onResumeNative(mNativeHandle); 219 } 220 221 @Override 222 protected void onSaveInstanceState(Bundle outState) { 223 super.onSaveInstanceState(outState); 224 byte[] state = onSaveInstanceStateNative(mNativeHandle); 225 if (state != null) { 226 outState.putByteArray(KEY_NATIVE_SAVED_STATE, state); 227 } 228 } 229 230 @Override 231 protected void onStart() { 232 super.onStart(); 233 onStartNative(mNativeHandle); 234 } 235 236 @Override 237 protected void onStop() { 238 super.onStop(); 239 onStopNative(mNativeHandle); 240 } 241 242 @Override 243 public void onConfigurationChanged(Configuration newConfig) { 244 super.onConfigurationChanged(newConfig); 245 if (!mDestroyed) { 246 onConfigurationChangedNative(mNativeHandle); 247 } 248 } 249 250 @Override 251 public void onLowMemory() { 252 super.onLowMemory(); 253 if (!mDestroyed) { 254 onLowMemoryNative(mNativeHandle); 255 } 256 } 257 258 @Override 259 public void onWindowFocusChanged(boolean hasFocus) { 260 super.onWindowFocusChanged(hasFocus); 261 if (!mDestroyed) { 262 onWindowFocusChangedNative(mNativeHandle, hasFocus); 263 } 264 } 265 266 @Override 267 public boolean dispatchKeyEvent(KeyEvent event) { 268 if (mDispatchingUnhandledKey) { 269 return super.dispatchKeyEvent(event); 270 } else { 271 // Key events from the IME do not go through the input channel; 272 // we need to intercept them here to hand to the application. 273 dispatchKeyEventNative(mNativeHandle, event); 274 return true; 275 } 276 } 277 278 public void surfaceCreated(SurfaceHolder holder) { 279 if (!mDestroyed) { 280 mCurSurfaceHolder = holder; 281 onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); 282 } 283 } 284 285 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 286 if (!mDestroyed) { 287 mCurSurfaceHolder = holder; 288 onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); 289 } 290 } 291 292 public void surfaceRedrawNeeded(SurfaceHolder holder) { 293 if (!mDestroyed) { 294 mCurSurfaceHolder = holder; 295 onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); 296 } 297 } 298 299 public void surfaceDestroyed(SurfaceHolder holder) { 300 mCurSurfaceHolder = null; 301 if (!mDestroyed) { 302 onSurfaceDestroyedNative(mNativeHandle); 303 } 304 } 305 306 public void onInputQueueCreated(InputQueue queue) { 307 if (!mDestroyed) { 308 mCurInputQueue = queue; 309 onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel()); 310 } 311 } 312 313 public void onInputQueueDestroyed(InputQueue queue) { 314 mCurInputQueue = null; 315 if (!mDestroyed) { 316 onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel()); 317 } 318 } 319 320 public void onGlobalLayout() { 321 mNativeContentView.getLocationInWindow(mLocation); 322 int w = mNativeContentView.getWidth(); 323 int h = mNativeContentView.getHeight(); 324 if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY 325 || w != mLastContentWidth || h != mLastContentHeight) { 326 mLastContentX = mLocation[0]; 327 mLastContentY = mLocation[1]; 328 mLastContentWidth = w; 329 mLastContentHeight = h; 330 if (!mDestroyed) { 331 onContentRectChangedNative(mNativeHandle, mLastContentX, 332 mLastContentY, mLastContentWidth, mLastContentHeight); 333 } 334 } 335 } 336 337 void dispatchUnhandledKeyEvent(KeyEvent event) { 338 try { 339 mDispatchingUnhandledKey = true; 340 View decor = getWindow().getDecorView(); 341 if (decor != null) { 342 decor.dispatchKeyEvent(event); 343 } 344 } finally { 345 mDispatchingUnhandledKey = false; 346 } 347 } 348 349 void preDispatchKeyEvent(KeyEvent event, int seq) { 350 mIMM.dispatchKeyEvent(this, seq, event, 351 mInputMethodCallback); 352 } 353 354 void setWindowFlags(int flags, int mask) { 355 getWindow().setFlags(flags, mask); 356 } 357 358 void setWindowFormat(int format) { 359 getWindow().setFormat(format); 360 } 361 362 void showIme(int mode) { 363 mIMM.showSoftInput(mNativeContentView, mode); 364 } 365 366 void hideIme(int mode) { 367 mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); 368 } 369} 370