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 int 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 94 private native int loadNativeCode(String path, String funcname, MessageQueue queue, 95 String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, 96 AssetManager assetMgr, byte[] savedState); 97 private native void unloadNativeCode(int handle); 98 99 private native void onStartNative(int handle); 100 private native void onResumeNative(int handle); 101 private native byte[] onSaveInstanceStateNative(int handle); 102 private native void onPauseNative(int handle); 103 private native void onStopNative(int handle); 104 private native void onConfigurationChangedNative(int handle); 105 private native void onLowMemoryNative(int handle); 106 private native void onWindowFocusChangedNative(int handle, boolean focused); 107 private native void onSurfaceCreatedNative(int handle, Surface surface); 108 private native void onSurfaceChangedNative(int handle, Surface surface, 109 int format, int width, int height); 110 private native void onSurfaceRedrawNeededNative(int handle, Surface surface); 111 private native void onSurfaceDestroyedNative(int handle); 112 private native void onInputQueueCreatedNative(int handle, int queuePtr); 113 private native void onInputQueueDestroyedNative(int handle, int queuePtr); 114 private native void onContentRectChangedNative(int handle, int x, int y, int w, int h); 115 116 static class NativeContentView extends View { 117 NativeActivity mActivity; 118 119 public NativeContentView(Context context) { 120 super(context); 121 } 122 123 public NativeContentView(Context context, AttributeSet attrs) { 124 super(context, attrs); 125 } 126 } 127 128 @Override 129 protected void onCreate(Bundle savedInstanceState) { 130 String libname = "main"; 131 String funcname = "ANativeActivity_onCreate"; 132 ActivityInfo ai; 133 134 mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 135 136 getWindow().takeSurface(this); 137 getWindow().takeInputQueue(this); 138 getWindow().setFormat(PixelFormat.RGB_565); 139 getWindow().setSoftInputMode( 140 WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED 141 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 142 143 mNativeContentView = new NativeContentView(this); 144 mNativeContentView.mActivity = this; 145 setContentView(mNativeContentView); 146 mNativeContentView.requestFocus(); 147 mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); 148 149 try { 150 ai = getPackageManager().getActivityInfo( 151 getIntent().getComponent(), PackageManager.GET_META_DATA); 152 if (ai.metaData != null) { 153 String ln = ai.metaData.getString(META_DATA_LIB_NAME); 154 if (ln != null) libname = ln; 155 ln = ai.metaData.getString(META_DATA_FUNC_NAME); 156 if (ln != null) funcname = ln; 157 } 158 } catch (PackageManager.NameNotFoundException e) { 159 throw new RuntimeException("Error getting activity info", e); 160 } 161 162 String path = null; 163 164 File libraryFile = new File(ai.applicationInfo.nativeLibraryDir, 165 System.mapLibraryName(libname)); 166 if (libraryFile.exists()) { 167 path = libraryFile.getPath(); 168 } 169 170 if (path == null) { 171 throw new IllegalArgumentException("Unable to find native library: " + libname); 172 } 173 174 byte[] nativeSavedState = savedInstanceState != null 175 ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; 176 177 mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), 178 getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), 179 getAbsolutePath(getExternalFilesDir(null)), 180 Build.VERSION.SDK_INT, getAssets(), nativeSavedState); 181 182 if (mNativeHandle == 0) { 183 throw new IllegalArgumentException("Unable to load native library: " + path); 184 } 185 super.onCreate(savedInstanceState); 186 } 187 188 private static String getAbsolutePath(File file) { 189 return (file != null) ? file.getAbsolutePath() : null; 190 } 191 192 @Override 193 protected void onDestroy() { 194 mDestroyed = true; 195 if (mCurSurfaceHolder != null) { 196 onSurfaceDestroyedNative(mNativeHandle); 197 mCurSurfaceHolder = null; 198 } 199 if (mCurInputQueue != null) { 200 onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr()); 201 mCurInputQueue = null; 202 } 203 unloadNativeCode(mNativeHandle); 204 super.onDestroy(); 205 } 206 207 @Override 208 protected void onPause() { 209 super.onPause(); 210 onPauseNative(mNativeHandle); 211 } 212 213 @Override 214 protected void onResume() { 215 super.onResume(); 216 onResumeNative(mNativeHandle); 217 } 218 219 @Override 220 protected void onSaveInstanceState(Bundle outState) { 221 super.onSaveInstanceState(outState); 222 byte[] state = onSaveInstanceStateNative(mNativeHandle); 223 if (state != null) { 224 outState.putByteArray(KEY_NATIVE_SAVED_STATE, state); 225 } 226 } 227 228 @Override 229 protected void onStart() { 230 super.onStart(); 231 onStartNative(mNativeHandle); 232 } 233 234 @Override 235 protected void onStop() { 236 super.onStop(); 237 onStopNative(mNativeHandle); 238 } 239 240 @Override 241 public void onConfigurationChanged(Configuration newConfig) { 242 super.onConfigurationChanged(newConfig); 243 if (!mDestroyed) { 244 onConfigurationChangedNative(mNativeHandle); 245 } 246 } 247 248 @Override 249 public void onLowMemory() { 250 super.onLowMemory(); 251 if (!mDestroyed) { 252 onLowMemoryNative(mNativeHandle); 253 } 254 } 255 256 @Override 257 public void onWindowFocusChanged(boolean hasFocus) { 258 super.onWindowFocusChanged(hasFocus); 259 if (!mDestroyed) { 260 onWindowFocusChangedNative(mNativeHandle, hasFocus); 261 } 262 } 263 264 public void surfaceCreated(SurfaceHolder holder) { 265 if (!mDestroyed) { 266 mCurSurfaceHolder = holder; 267 onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); 268 } 269 } 270 271 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 272 if (!mDestroyed) { 273 mCurSurfaceHolder = holder; 274 onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); 275 } 276 } 277 278 public void surfaceRedrawNeeded(SurfaceHolder holder) { 279 if (!mDestroyed) { 280 mCurSurfaceHolder = holder; 281 onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); 282 } 283 } 284 285 public void surfaceDestroyed(SurfaceHolder holder) { 286 mCurSurfaceHolder = null; 287 if (!mDestroyed) { 288 onSurfaceDestroyedNative(mNativeHandle); 289 } 290 } 291 292 public void onInputQueueCreated(InputQueue queue) { 293 if (!mDestroyed) { 294 mCurInputQueue = queue; 295 onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); 296 } 297 } 298 299 public void onInputQueueDestroyed(InputQueue queue) { 300 if (!mDestroyed) { 301 onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); 302 mCurInputQueue = null; 303 } 304 } 305 306 public void onGlobalLayout() { 307 mNativeContentView.getLocationInWindow(mLocation); 308 int w = mNativeContentView.getWidth(); 309 int h = mNativeContentView.getHeight(); 310 if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY 311 || w != mLastContentWidth || h != mLastContentHeight) { 312 mLastContentX = mLocation[0]; 313 mLastContentY = mLocation[1]; 314 mLastContentWidth = w; 315 mLastContentHeight = h; 316 if (!mDestroyed) { 317 onContentRectChangedNative(mNativeHandle, mLastContentX, 318 mLastContentY, mLastContentWidth, mLastContentHeight); 319 } 320 } 321 } 322 323 void setWindowFlags(int flags, int mask) { 324 getWindow().setFlags(flags, mask); 325 } 326 327 void setWindowFormat(int format) { 328 getWindow().setFormat(format); 329 } 330 331 void showIme(int mode) { 332 mIMM.showSoftInput(mNativeContentView, mode); 333 } 334 335 void hideIme(int mode) { 336 mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); 337 } 338 } 339