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