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