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 17package android.app; 18 19import android.content.Context; 20import android.content.pm.ActivityInfo; 21import android.content.pm.PackageManager; 22import android.content.res.AssetManager; 23import android.content.res.Configuration; 24import android.graphics.PixelFormat; 25import android.os.Build; 26import android.os.Bundle; 27import android.os.Looper; 28import android.os.MessageQueue; 29import android.util.AttributeSet; 30import android.view.InputQueue; 31import android.view.Surface; 32import android.view.SurfaceHolder; 33import android.view.View; 34import android.view.ViewTreeObserver.OnGlobalLayoutListener; 35import android.view.WindowManager; 36import android.view.inputmethod.InputMethodManager; 37 38import 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 */ 57public 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 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); 97 private native void unloadNativeCode(long handle); 98 private native void onStartNative(long handle); 99 private native void onResumeNative(long handle); 100 private native byte[] onSaveInstanceStateNative(long handle); 101 private native void onPauseNative(long handle); 102 private native void onStopNative(long handle); 103 private native void onConfigurationChangedNative(long handle); 104 private native void onLowMemoryNative(long handle); 105 private native void onWindowFocusChangedNative(long handle, boolean focused); 106 private native void onSurfaceCreatedNative(long handle, Surface surface); 107 private native void onSurfaceChangedNative(long handle, Surface surface, 108 int format, int width, int height); 109 private native void onSurfaceRedrawNeededNative(long handle, Surface surface); 110 private native void onSurfaceDestroyedNative(long handle); 111 private native void onInputQueueCreatedNative(long handle, long queuePtr); 112 private native void onInputQueueDestroyedNative(long handle, long queuePtr); 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 118 public NativeContentView(Context context) { 119 super(context); 120 } 121 122 public NativeContentView(Context context, AttributeSet attrs) { 123 super(context, attrs); 124 } 125 } 126 127 @Override 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 187 private static String getAbsolutePath(File file) { 188 return (file != null) ? file.getAbsolutePath() : null; 189 } 190 191 @Override 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 207 protected void onPause() { 208 super.onPause(); 209 onPauseNative(mNativeHandle); 210 } 211 212 @Override 213 protected void onResume() { 214 super.onResume(); 215 onResumeNative(mNativeHandle); 216 } 217 218 @Override 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 228 protected void onStart() { 229 super.onStart(); 230 onStartNative(mNativeHandle); 231 } 232 233 @Override 234 protected void onStop() { 235 super.onStop(); 236 onStopNative(mNativeHandle); 237 } 238 239 @Override 240 public void onConfigurationChanged(Configuration newConfig) { 241 super.onConfigurationChanged(newConfig); 242 if (!mDestroyed) { 243 onConfigurationChangedNative(mNativeHandle); 244 } 245 } 246 247 @Override 248 public void onLowMemory() { 249 super.onLowMemory(); 250 if (!mDestroyed) { 251 onLowMemoryNative(mNativeHandle); 252 } 253 } 254 255 @Override 256 public void onWindowFocusChanged(boolean hasFocus) { 257 super.onWindowFocusChanged(hasFocus); 258 if (!mDestroyed) { 259 onWindowFocusChangedNative(mNativeHandle, hasFocus); 260 } 261 } 262 263 public void surfaceCreated(SurfaceHolder holder) { 264 if (!mDestroyed) { 265 mCurSurfaceHolder = holder; 266 onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); 267 } 268 } 269 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 277 public void surfaceRedrawNeeded(SurfaceHolder holder) { 278 if (!mDestroyed) { 279 mCurSurfaceHolder = holder; 280 onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); 281 } 282 } 283 284 public void surfaceDestroyed(SurfaceHolder holder) { 285 mCurSurfaceHolder = null; 286 if (!mDestroyed) { 287 onSurfaceDestroyedNative(mNativeHandle); 288 } 289 } 290 291 public void onInputQueueCreated(InputQueue queue) { 292 if (!mDestroyed) { 293 mCurInputQueue = queue; 294 onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); 295 } 296 } 297 298 public void onInputQueueDestroyed(InputQueue queue) { 299 if (!mDestroyed) { 300 onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); 301 mCurInputQueue = null; 302 } 303 } 304 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 322 void setWindowFlags(int flags, int mask) { 323 getWindow().setFlags(flags, mask); 324 } 325 326 void setWindowFormat(int format) { 327 getWindow().setFormat(format); 328 } 329 330 void showIme(int mode) { 331 mIMM.showSoftInput(mNativeContentView, mode); 332 } 333 334 void hideIme(int mode) { 335 mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); 336 } 337} 338