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