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