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 dalvik.system.BaseDexClassLoader; 39 40 import java.io.File; 41 42 /** 43 * Convenience for implementing an activity that will be implemented 44 * purely in native code. That is, a game (or game-like thing). There 45 * is no need to derive from this class; you can simply declare it in your 46 * manifest, and use the NDK APIs from there. 47 * 48 * <p>A typical manifest would look like: 49 * 50 * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml 51 * manifest} 52 * 53 * <p>A very simple example of native code that is run by NativeActivity 54 * follows. This reads input events from the user and uses OpenGLES to 55 * draw into the native activity's window. 56 * 57 * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all} 58 */ 59 public class NativeActivity extends Activity implements SurfaceHolder.Callback2, 60 InputQueue.Callback, OnGlobalLayoutListener { 61 /** 62 * Optional meta-that can be in the manifest for this component, specifying 63 * the name of the native shared library to load. If not specified, 64 * "main" is used. 65 */ 66 public static final String META_DATA_LIB_NAME = "android.app.lib_name"; 67 68 /** 69 * Optional meta-that can be in the manifest for this component, specifying 70 * the name of the main entry point for this native activity in the 71 * {@link #META_DATA_LIB_NAME} native code. If not specified, 72 * "ANativeActivity_onCreate" is used. 73 */ 74 public static final String META_DATA_FUNC_NAME = "android.app.func_name"; 75 76 private static final String KEY_NATIVE_SAVED_STATE = "android:native_state"; 77 78 private NativeContentView mNativeContentView; 79 private InputMethodManager mIMM; 80 81 private long mNativeHandle; 82 83 private InputQueue mCurInputQueue; 84 private SurfaceHolder mCurSurfaceHolder; 85 86 final int[] mLocation = new int[2]; 87 int mLastContentX; 88 int mLastContentY; 89 int mLastContentWidth; 90 int mLastContentHeight; 91 92 private boolean mDispatchingUnhandledKey; 93 94 private boolean mDestroyed; 95 96 private native long loadNativeCode(String path, String funcname, MessageQueue queue, 97 String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, 98 AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath); 99 private native String getDlError(); 100 private native void unloadNativeCode(long handle); 101 private native void onStartNative(long handle); 102 private native void onResumeNative(long handle); 103 private native byte[] onSaveInstanceStateNative(long handle); 104 private native void onPauseNative(long handle); 105 private native void onStopNative(long handle); 106 private native void onConfigurationChangedNative(long handle); 107 private native void onLowMemoryNative(long handle); 108 private native void onWindowFocusChangedNative(long handle, boolean focused); 109 private native void onSurfaceCreatedNative(long handle, Surface surface); 110 private native void onSurfaceChangedNative(long handle, Surface surface, 111 int format, int width, int height); 112 private native void onSurfaceRedrawNeededNative(long handle, Surface surface); 113 private native void onSurfaceDestroyedNative(long handle); 114 private native void onInputQueueCreatedNative(long handle, long queuePtr); 115 private native void onInputQueueDestroyedNative(long handle, long queuePtr); 116 private native void onContentRectChangedNative(long handle, int x, int y, int w, int h); 117 118 static class NativeContentView extends View { 119 NativeActivity mActivity; 120 121 public NativeContentView(Context context) { 122 super(context); 123 } 124 125 public NativeContentView(Context context, AttributeSet attrs) { 126 super(context, attrs); 127 } 128 } 129 130 @Override 131 protected void onCreate(Bundle savedInstanceState) { 132 String libname = "main"; 133 String funcname = "ANativeActivity_onCreate"; 134 ActivityInfo ai; 135 136 mIMM = getSystemService(InputMethodManager.class); 137 138 getWindow().takeSurface(this); 139 getWindow().takeInputQueue(this); 140 getWindow().setFormat(PixelFormat.RGB_565); 141 getWindow().setSoftInputMode( 142 WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED 143 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 144 145 mNativeContentView = new NativeContentView(this); 146 mNativeContentView.mActivity = this; 147 setContentView(mNativeContentView); 148 mNativeContentView.requestFocus(); 149 mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); 150 151 try { 152 ai = getPackageManager().getActivityInfo( 153 getIntent().getComponent(), PackageManager.GET_META_DATA); 154 if (ai.metaData != null) { 155 String ln = ai.metaData.getString(META_DATA_LIB_NAME); 156 if (ln != null) libname = ln; 157 ln = ai.metaData.getString(META_DATA_FUNC_NAME); 158 if (ln != null) funcname = ln; 159 } 160 } catch (PackageManager.NameNotFoundException e) { 161 throw new RuntimeException("Error getting activity info", e); 162 } 163 164 BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader(); 165 String path = classLoader.findLibrary(libname); 166 167 if (path == null) { 168 throw new IllegalArgumentException("Unable to find native library " + libname + 169 " using classloader: " + classLoader.toString()); 170 } 171 172 byte[] nativeSavedState = savedInstanceState != null 173 ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; 174 175 mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), 176 getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), 177 getAbsolutePath(getExternalFilesDir(null)), 178 Build.VERSION.SDK_INT, getAssets(), nativeSavedState, 179 classLoader, classLoader.getLdLibraryPath()); 180 181 if (mNativeHandle == 0) { 182 throw new UnsatisfiedLinkError( 183 "Unable to load native library \"" + path + "\": " + getDlError()); 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