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