• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
loadNativeCode(String path, String funcname, MessageQueue queue, String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, AssetManager assetMgr, byte[] savedState)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);
unloadNativeCode(int handle)89     private native void unloadNativeCode(int handle);
90 
onStartNative(int handle)91     private native void onStartNative(int handle);
onResumeNative(int handle)92     private native void onResumeNative(int handle);
onSaveInstanceStateNative(int handle)93     private native byte[] onSaveInstanceStateNative(int handle);
onPauseNative(int handle)94     private native void onPauseNative(int handle);
onStopNative(int handle)95     private native void onStopNative(int handle);
onConfigurationChangedNative(int handle)96     private native void onConfigurationChangedNative(int handle);
onLowMemoryNative(int handle)97     private native void onLowMemoryNative(int handle);
onWindowFocusChangedNative(int handle, boolean focused)98     private native void onWindowFocusChangedNative(int handle, boolean focused);
onSurfaceCreatedNative(int handle, Surface surface)99     private native void onSurfaceCreatedNative(int handle, Surface surface);
onSurfaceChangedNative(int handle, Surface surface, int format, int width, int height)100     private native void onSurfaceChangedNative(int handle, Surface surface,
101             int format, int width, int height);
onSurfaceRedrawNeededNative(int handle, Surface surface)102     private native void onSurfaceRedrawNeededNative(int handle, Surface surface);
onSurfaceDestroyedNative(int handle)103     private native void onSurfaceDestroyedNative(int handle);
onInputChannelCreatedNative(int handle, InputChannel channel)104     private native void onInputChannelCreatedNative(int handle, InputChannel channel);
onInputChannelDestroyedNative(int handle, InputChannel channel)105     private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
onContentRectChangedNative(int handle, int x, int y, int w, int h)106     private native void onContentRectChangedNative(int handle, int x, int y, int w, int h);
dispatchKeyEventNative(int handle, KeyEvent event)107     private native void dispatchKeyEventNative(int handle, KeyEvent event);
finishPreDispatchKeyEventNative(int handle, int seq, boolean handled)108     private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled);
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     static class InputMethodCallback extends IInputMethodCallback.Stub {
123         WeakReference<NativeActivity> mNa;
124 
InputMethodCallback(NativeActivity na)125         InputMethodCallback(NativeActivity na) {
126             mNa = new WeakReference<NativeActivity>(na);
127         }
128 
129         @Override
finishedEvent(int seq, boolean handled)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
sessionCreated(IInputMethodSession session)138         public void sessionCreated(IInputMethodSession session) {
139             // Stub -- not for use in the client.
140         }
141     }
142 
143     @Override
onCreate(Bundle savedInstanceState)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
onDestroy()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
onPause()220     protected void onPause() {
221         super.onPause();
222         onPauseNative(mNativeHandle);
223     }
224 
225     @Override
onResume()226     protected void onResume() {
227         super.onResume();
228         onResumeNative(mNativeHandle);
229     }
230 
231     @Override
onSaveInstanceState(Bundle outState)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
onStart()241     protected void onStart() {
242         super.onStart();
243         onStartNative(mNativeHandle);
244     }
245 
246     @Override
onStop()247     protected void onStop() {
248         super.onStop();
249         onStopNative(mNativeHandle);
250     }
251 
252     @Override
onConfigurationChanged(Configuration newConfig)253     public void onConfigurationChanged(Configuration newConfig) {
254         super.onConfigurationChanged(newConfig);
255         if (!mDestroyed) {
256             onConfigurationChangedNative(mNativeHandle);
257         }
258     }
259 
260     @Override
onLowMemory()261     public void onLowMemory() {
262         super.onLowMemory();
263         if (!mDestroyed) {
264             onLowMemoryNative(mNativeHandle);
265         }
266     }
267 
268     @Override
onWindowFocusChanged(boolean hasFocus)269     public void onWindowFocusChanged(boolean hasFocus) {
270         super.onWindowFocusChanged(hasFocus);
271         if (!mDestroyed) {
272             onWindowFocusChangedNative(mNativeHandle, hasFocus);
273         }
274     }
275 
276     @Override
dispatchKeyEvent(KeyEvent event)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 
surfaceCreated(SurfaceHolder holder)288     public void surfaceCreated(SurfaceHolder holder) {
289         if (!mDestroyed) {
290             mCurSurfaceHolder = holder;
291             onSurfaceCreatedNative(mNativeHandle, holder.getSurface());
292         }
293     }
294 
surfaceChanged(SurfaceHolder holder, int format, int width, int height)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 
surfaceRedrawNeeded(SurfaceHolder holder)302     public void surfaceRedrawNeeded(SurfaceHolder holder) {
303         if (!mDestroyed) {
304             mCurSurfaceHolder = holder;
305             onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface());
306         }
307     }
308 
surfaceDestroyed(SurfaceHolder holder)309     public void surfaceDestroyed(SurfaceHolder holder) {
310         mCurSurfaceHolder = null;
311         if (!mDestroyed) {
312             onSurfaceDestroyedNative(mNativeHandle);
313         }
314     }
315 
onInputQueueCreated(InputQueue queue)316     public void onInputQueueCreated(InputQueue queue) {
317         if (!mDestroyed) {
318             mCurInputQueue = queue;
319             onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
320         }
321     }
322 
onInputQueueDestroyed(InputQueue queue)323     public void onInputQueueDestroyed(InputQueue queue) {
324         mCurInputQueue = null;
325         if (!mDestroyed) {
326             onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
327         }
328     }
329 
onGlobalLayout()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 
dispatchUnhandledKeyEvent(KeyEvent event)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 
preDispatchKeyEvent(KeyEvent event, int seq)361     void preDispatchKeyEvent(KeyEvent event, int seq) {
362         mIMM.dispatchKeyEvent(this, seq, event,
363                 mInputMethodCallback);
364     }
365 
setWindowFlags(int flags, int mask)366     void setWindowFlags(int flags, int mask) {
367         getWindow().setFlags(flags, mask);
368     }
369 
setWindowFormat(int format)370     void setWindowFormat(int format) {
371         getWindow().setFormat(format);
372     }
373 
showIme(int mode)374     void showIme(int mode) {
375         mIMM.showSoftInput(mNativeContentView, mode);
376     }
377 
hideIme(int mode)378     void hideIme(int mode) {
379         mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode);
380     }
381 }
382