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