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 <a href="https://github.com/googlesamples/android-ndk/tree/master/native-activity">sample 49 * native activity</a> is available in the NDK samples. 50 */ 51 public class NativeActivity extends Activity implements SurfaceHolder.Callback2, 52 InputQueue.Callback, OnGlobalLayoutListener { 53 /** 54 * Optional meta-that can be in the manifest for this component, specifying 55 * the name of the native shared library to load. If not specified, 56 * "main" is used. 57 */ 58 public static final String META_DATA_LIB_NAME = "android.app.lib_name"; 59 60 /** 61 * Optional meta-that can be in the manifest for this component, specifying 62 * the name of the main entry point for this native activity in the 63 * {@link #META_DATA_LIB_NAME} native code. If not specified, 64 * "ANativeActivity_onCreate" is used. 65 */ 66 public static final String META_DATA_FUNC_NAME = "android.app.func_name"; 67 68 private static final String KEY_NATIVE_SAVED_STATE = "android:native_state"; 69 70 private NativeContentView mNativeContentView; 71 private InputMethodManager mIMM; 72 73 private long mNativeHandle; 74 75 private InputQueue mCurInputQueue; 76 private SurfaceHolder mCurSurfaceHolder; 77 78 final int[] mLocation = new int[2]; 79 int mLastContentX; 80 int mLastContentY; 81 int mLastContentWidth; 82 int mLastContentHeight; 83 84 private boolean mDispatchingUnhandledKey; 85 86 private boolean mDestroyed; 87 loadNativeCode(String path, String funcname, MessageQueue queue, String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath)88 private native long loadNativeCode(String path, String funcname, MessageQueue queue, 89 String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, 90 AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath); getDlError()91 private native String getDlError(); unloadNativeCode(long handle)92 private native void unloadNativeCode(long handle); onStartNative(long handle)93 private native void onStartNative(long handle); onResumeNative(long handle)94 private native void onResumeNative(long handle); onSaveInstanceStateNative(long handle)95 private native byte[] onSaveInstanceStateNative(long handle); onPauseNative(long handle)96 private native void onPauseNative(long handle); onStopNative(long handle)97 private native void onStopNative(long handle); onConfigurationChangedNative(long handle)98 private native void onConfigurationChangedNative(long handle); onLowMemoryNative(long handle)99 private native void onLowMemoryNative(long handle); onWindowFocusChangedNative(long handle, boolean focused)100 private native void onWindowFocusChangedNative(long handle, boolean focused); onSurfaceCreatedNative(long handle, Surface surface)101 private native void onSurfaceCreatedNative(long handle, Surface surface); onSurfaceChangedNative(long handle, Surface surface, int format, int width, int height)102 private native void onSurfaceChangedNative(long handle, Surface surface, 103 int format, int width, int height); onSurfaceRedrawNeededNative(long handle, Surface surface)104 private native void onSurfaceRedrawNeededNative(long handle, Surface surface); onSurfaceDestroyedNative(long handle)105 private native void onSurfaceDestroyedNative(long handle); onInputQueueCreatedNative(long handle, long queuePtr)106 private native void onInputQueueCreatedNative(long handle, long queuePtr); onInputQueueDestroyedNative(long handle, long queuePtr)107 private native void onInputQueueDestroyedNative(long handle, long queuePtr); onContentRectChangedNative(long handle, int x, int y, int w, int h)108 private native void onContentRectChangedNative(long handle, int x, int y, int w, int h); 109 110 static class NativeContentView extends View { 111 NativeActivity mActivity; 112 NativeContentView(Context context)113 public NativeContentView(Context context) { 114 super(context); 115 } 116 NativeContentView(Context context, AttributeSet attrs)117 public NativeContentView(Context context, AttributeSet attrs) { 118 super(context, attrs); 119 } 120 } 121 122 @Override onCreate(Bundle savedInstanceState)123 protected void onCreate(Bundle savedInstanceState) { 124 String libname = "main"; 125 String funcname = "ANativeActivity_onCreate"; 126 ActivityInfo ai; 127 128 mIMM = getSystemService(InputMethodManager.class); 129 130 getWindow().takeSurface(this); 131 getWindow().takeInputQueue(this); 132 getWindow().setFormat(PixelFormat.RGB_565); 133 getWindow().setSoftInputMode( 134 WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED 135 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 136 137 mNativeContentView = new NativeContentView(this); 138 mNativeContentView.mActivity = this; 139 setContentView(mNativeContentView); 140 mNativeContentView.requestFocus(); 141 mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); 142 143 try { 144 ai = getPackageManager().getActivityInfo( 145 getIntent().getComponent(), PackageManager.GET_META_DATA); 146 if (ai.metaData != null) { 147 String ln = ai.metaData.getString(META_DATA_LIB_NAME); 148 if (ln != null) libname = ln; 149 ln = ai.metaData.getString(META_DATA_FUNC_NAME); 150 if (ln != null) funcname = ln; 151 } 152 } catch (PackageManager.NameNotFoundException e) { 153 throw new RuntimeException("Error getting activity info", e); 154 } 155 156 BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader(); 157 String path = classLoader.findLibrary(libname); 158 159 if (path == null) { 160 throw new IllegalArgumentException("Unable to find native library " + libname + 161 " using classloader: " + classLoader.toString()); 162 } 163 164 byte[] nativeSavedState = savedInstanceState != null 165 ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; 166 167 mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), 168 getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), 169 getAbsolutePath(getExternalFilesDir(null)), 170 Build.VERSION.SDK_INT, getAssets(), nativeSavedState, 171 classLoader, classLoader.getLdLibraryPath()); 172 173 if (mNativeHandle == 0) { 174 throw new UnsatisfiedLinkError( 175 "Unable to load native library \"" + path + "\": " + getDlError()); 176 } 177 super.onCreate(savedInstanceState); 178 } 179 getAbsolutePath(File file)180 private static String getAbsolutePath(File file) { 181 return (file != null) ? file.getAbsolutePath() : null; 182 } 183 184 @Override onDestroy()185 protected void onDestroy() { 186 mDestroyed = true; 187 if (mCurSurfaceHolder != null) { 188 onSurfaceDestroyedNative(mNativeHandle); 189 mCurSurfaceHolder = null; 190 } 191 if (mCurInputQueue != null) { 192 onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr()); 193 mCurInputQueue = null; 194 } 195 unloadNativeCode(mNativeHandle); 196 super.onDestroy(); 197 } 198 199 @Override onPause()200 protected void onPause() { 201 super.onPause(); 202 onPauseNative(mNativeHandle); 203 } 204 205 @Override onResume()206 protected void onResume() { 207 super.onResume(); 208 onResumeNative(mNativeHandle); 209 } 210 211 @Override onSaveInstanceState(Bundle outState)212 protected void onSaveInstanceState(Bundle outState) { 213 super.onSaveInstanceState(outState); 214 byte[] state = onSaveInstanceStateNative(mNativeHandle); 215 if (state != null) { 216 outState.putByteArray(KEY_NATIVE_SAVED_STATE, state); 217 } 218 } 219 220 @Override onStart()221 protected void onStart() { 222 super.onStart(); 223 onStartNative(mNativeHandle); 224 } 225 226 @Override onStop()227 protected void onStop() { 228 super.onStop(); 229 onStopNative(mNativeHandle); 230 } 231 232 @Override onConfigurationChanged(Configuration newConfig)233 public void onConfigurationChanged(Configuration newConfig) { 234 super.onConfigurationChanged(newConfig); 235 if (!mDestroyed) { 236 onConfigurationChangedNative(mNativeHandle); 237 } 238 } 239 240 @Override onLowMemory()241 public void onLowMemory() { 242 super.onLowMemory(); 243 if (!mDestroyed) { 244 onLowMemoryNative(mNativeHandle); 245 } 246 } 247 248 @Override onWindowFocusChanged(boolean hasFocus)249 public void onWindowFocusChanged(boolean hasFocus) { 250 super.onWindowFocusChanged(hasFocus); 251 if (!mDestroyed) { 252 onWindowFocusChangedNative(mNativeHandle, hasFocus); 253 } 254 } 255 surfaceCreated(SurfaceHolder holder)256 public void surfaceCreated(SurfaceHolder holder) { 257 if (!mDestroyed) { 258 mCurSurfaceHolder = holder; 259 onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); 260 } 261 } 262 surfaceChanged(SurfaceHolder holder, int format, int width, int height)263 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 264 if (!mDestroyed) { 265 mCurSurfaceHolder = holder; 266 onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); 267 } 268 } 269 surfaceRedrawNeeded(SurfaceHolder holder)270 public void surfaceRedrawNeeded(SurfaceHolder holder) { 271 if (!mDestroyed) { 272 mCurSurfaceHolder = holder; 273 onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); 274 } 275 } 276 surfaceDestroyed(SurfaceHolder holder)277 public void surfaceDestroyed(SurfaceHolder holder) { 278 mCurSurfaceHolder = null; 279 if (!mDestroyed) { 280 onSurfaceDestroyedNative(mNativeHandle); 281 } 282 } 283 onInputQueueCreated(InputQueue queue)284 public void onInputQueueCreated(InputQueue queue) { 285 if (!mDestroyed) { 286 mCurInputQueue = queue; 287 onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); 288 } 289 } 290 onInputQueueDestroyed(InputQueue queue)291 public void onInputQueueDestroyed(InputQueue queue) { 292 if (!mDestroyed) { 293 onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); 294 mCurInputQueue = null; 295 } 296 } 297 onGlobalLayout()298 public void onGlobalLayout() { 299 mNativeContentView.getLocationInWindow(mLocation); 300 int w = mNativeContentView.getWidth(); 301 int h = mNativeContentView.getHeight(); 302 if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY 303 || w != mLastContentWidth || h != mLastContentHeight) { 304 mLastContentX = mLocation[0]; 305 mLastContentY = mLocation[1]; 306 mLastContentWidth = w; 307 mLastContentHeight = h; 308 if (!mDestroyed) { 309 onContentRectChangedNative(mNativeHandle, mLastContentX, 310 mLastContentY, mLastContentWidth, mLastContentHeight); 311 } 312 } 313 } 314 setWindowFlags(int flags, int mask)315 void setWindowFlags(int flags, int mask) { 316 getWindow().setFlags(flags, mask); 317 } 318 setWindowFormat(int format)319 void setWindowFormat(int format) { 320 getWindow().setFormat(format); 321 } 322 showIme(int mode)323 void showIme(int mode) { 324 mIMM.showSoftInput(mNativeContentView, mode); 325 } 326 hideIme(int mode)327 void hideIme(int mode) { 328 mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); 329 } 330 } 331