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