• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
loadNativeCode(String path, String funcname, MessageQueue queue, String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, AssetManager assetMgr, byte[] savedState)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);
unloadNativeCode(int handle)88     private native void unloadNativeCode(int handle);
89 
onStartNative(int handle)90     private native void onStartNative(int handle);
onResumeNative(int handle)91     private native void onResumeNative(int handle);
onSaveInstanceStateNative(int handle)92     private native byte[] onSaveInstanceStateNative(int handle);
onPauseNative(int handle)93     private native void onPauseNative(int handle);
onStopNative(int handle)94     private native void onStopNative(int handle);
onConfigurationChangedNative(int handle)95     private native void onConfigurationChangedNative(int handle);
onLowMemoryNative(int handle)96     private native void onLowMemoryNative(int handle);
onWindowFocusChangedNative(int handle, boolean focused)97     private native void onWindowFocusChangedNative(int handle, boolean focused);
onSurfaceCreatedNative(int handle, Surface surface)98     private native void onSurfaceCreatedNative(int handle, Surface surface);
onSurfaceChangedNative(int handle, Surface surface, int format, int width, int height)99     private native void onSurfaceChangedNative(int handle, Surface surface,
100             int format, int width, int height);
onSurfaceRedrawNeededNative(int handle, Surface surface)101     private native void onSurfaceRedrawNeededNative(int handle, Surface surface);
onSurfaceDestroyedNative(int handle)102     private native void onSurfaceDestroyedNative(int handle);
onInputChannelCreatedNative(int handle, InputChannel channel)103     private native void onInputChannelCreatedNative(int handle, InputChannel channel);
onInputChannelDestroyedNative(int handle, InputChannel channel)104     private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
onContentRectChangedNative(int handle, int x, int y, int w, int h)105     private native void onContentRectChangedNative(int handle, int x, int y, int w, int h);
dispatchKeyEventNative(int handle, KeyEvent event)106     private native void dispatchKeyEventNative(int handle, KeyEvent event);
finishPreDispatchKeyEventNative(int handle, int seq, boolean handled)107     private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled);
108 
109     static class NativeContentView extends View {
110         NativeActivity mActivity;
111 
NativeContentView(Context context)112         public NativeContentView(Context context) {
113             super(context);
114         }
115 
NativeContentView(Context context, AttributeSet attrs)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 
InputMethodCallback(NativeActivity na)124         InputMethodCallback(NativeActivity na) {
125             mNa = new WeakReference<NativeActivity>(na);
126         }
127 
128         @Override
finishedEvent(int seq, boolean handled)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
onCreate(Bundle savedInstanceState)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
onDestroy()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
onPause()214     protected void onPause() {
215         super.onPause();
216         onPauseNative(mNativeHandle);
217     }
218 
219     @Override
onResume()220     protected void onResume() {
221         super.onResume();
222         onResumeNative(mNativeHandle);
223     }
224 
225     @Override
onSaveInstanceState(Bundle outState)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
onStart()235     protected void onStart() {
236         super.onStart();
237         onStartNative(mNativeHandle);
238     }
239 
240     @Override
onStop()241     protected void onStop() {
242         super.onStop();
243         onStopNative(mNativeHandle);
244     }
245 
246     @Override
onConfigurationChanged(Configuration newConfig)247     public void onConfigurationChanged(Configuration newConfig) {
248         super.onConfigurationChanged(newConfig);
249         if (!mDestroyed) {
250             onConfigurationChangedNative(mNativeHandle);
251         }
252     }
253 
254     @Override
onLowMemory()255     public void onLowMemory() {
256         super.onLowMemory();
257         if (!mDestroyed) {
258             onLowMemoryNative(mNativeHandle);
259         }
260     }
261 
262     @Override
onWindowFocusChanged(boolean hasFocus)263     public void onWindowFocusChanged(boolean hasFocus) {
264         super.onWindowFocusChanged(hasFocus);
265         if (!mDestroyed) {
266             onWindowFocusChangedNative(mNativeHandle, hasFocus);
267         }
268     }
269 
270     @Override
dispatchKeyEvent(KeyEvent event)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 
surfaceCreated(SurfaceHolder holder)282     public void surfaceCreated(SurfaceHolder holder) {
283         if (!mDestroyed) {
284             mCurSurfaceHolder = holder;
285             onSurfaceCreatedNative(mNativeHandle, holder.getSurface());
286         }
287     }
288 
surfaceChanged(SurfaceHolder holder, int format, int width, int height)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 
surfaceRedrawNeeded(SurfaceHolder holder)296     public void surfaceRedrawNeeded(SurfaceHolder holder) {
297         if (!mDestroyed) {
298             mCurSurfaceHolder = holder;
299             onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface());
300         }
301     }
302 
surfaceDestroyed(SurfaceHolder holder)303     public void surfaceDestroyed(SurfaceHolder holder) {
304         mCurSurfaceHolder = null;
305         if (!mDestroyed) {
306             onSurfaceDestroyedNative(mNativeHandle);
307         }
308     }
309 
onInputQueueCreated(InputQueue queue)310     public void onInputQueueCreated(InputQueue queue) {
311         if (!mDestroyed) {
312             mCurInputQueue = queue;
313             onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
314         }
315     }
316 
onInputQueueDestroyed(InputQueue queue)317     public void onInputQueueDestroyed(InputQueue queue) {
318         mCurInputQueue = null;
319         if (!mDestroyed) {
320             onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
321         }
322     }
323 
onGlobalLayout()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 
dispatchUnhandledKeyEvent(KeyEvent event)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 
preDispatchKeyEvent(KeyEvent event, int seq)355     void preDispatchKeyEvent(KeyEvent event, int seq) {
356         mIMM.dispatchKeyEvent(this, seq, event,
357                 mInputMethodCallback);
358     }
359 
setWindowFlags(int flags, int mask)360     void setWindowFlags(int flags, int mask) {
361         getWindow().setFlags(flags, mask);
362     }
363 
setWindowFormat(int format)364     void setWindowFormat(int format) {
365         getWindow().setFormat(format);
366     }
367 
showIme(int mode)368     void showIme(int mode) {
369         mIMM.showSoftInput(mNativeContentView, mode);
370     }
371 
hideIme(int mode)372     void hideIme(int mode) {
373         mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode);
374     }
375 }
376