| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.app; |
| |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.Context; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageManager; |
| import android.content.res.AssetManager; |
| import android.content.res.Configuration; |
| import android.graphics.PixelFormat; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Looper; |
| import android.os.MessageQueue; |
| import android.util.AttributeSet; |
| import android.view.InputQueue; |
| import android.view.Surface; |
| import android.view.SurfaceHolder; |
| import android.view.View; |
| import android.view.ViewTreeObserver.OnGlobalLayoutListener; |
| import android.view.WindowManager; |
| import android.view.inputmethod.InputMethodManager; |
| |
| import dalvik.system.BaseDexClassLoader; |
| |
| import java.io.File; |
| |
| /** |
| * Convenience for implementing an activity that will be implemented |
| * purely in native code. That is, a game (or game-like thing). There |
| * is no need to derive from this class; you can simply declare it in your |
| * manifest, and use the NDK APIs from there. |
| * |
| * <p>A <a href="https://github.com/googlesamples/android-ndk/tree/master/native-activity">sample |
| * native activity</a> is available in the NDK samples. |
| */ |
| public class NativeActivity extends Activity implements SurfaceHolder.Callback2, |
| InputQueue.Callback, OnGlobalLayoutListener { |
| /** |
| * Optional meta-that can be in the manifest for this component, specifying |
| * the name of the native shared library to load. If not specified, |
| * "main" is used. |
| */ |
| public static final String META_DATA_LIB_NAME = "android.app.lib_name"; |
| |
| /** |
| * Optional meta-that can be in the manifest for this component, specifying |
| * the name of the main entry point for this native activity in the |
| * {@link #META_DATA_LIB_NAME} native code. If not specified, |
| * "ANativeActivity_onCreate" is used. |
| */ |
| public static final String META_DATA_FUNC_NAME = "android.app.func_name"; |
| |
| private static final String KEY_NATIVE_SAVED_STATE = "android:native_state"; |
| |
| private NativeContentView mNativeContentView; |
| private InputMethodManager mIMM; |
| |
| @UnsupportedAppUsage |
| private long mNativeHandle; |
| |
| private InputQueue mCurInputQueue; |
| private SurfaceHolder mCurSurfaceHolder; |
| |
| final int[] mLocation = new int[2]; |
| int mLastContentX; |
| int mLastContentY; |
| int mLastContentWidth; |
| int mLastContentHeight; |
| |
| private boolean mDispatchingUnhandledKey; |
| |
| private boolean mDestroyed; |
| |
| @UnsupportedAppUsage |
| private native long loadNativeCode(String path, String funcname, MessageQueue queue, |
| String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, |
| AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath); |
| private native String getDlError(); |
| private native void unloadNativeCode(long handle); |
| private native void onStartNative(long handle); |
| private native void onResumeNative(long handle); |
| private native byte[] onSaveInstanceStateNative(long handle); |
| private native void onPauseNative(long handle); |
| private native void onStopNative(long handle); |
| private native void onConfigurationChangedNative(long handle); |
| private native void onLowMemoryNative(long handle); |
| private native void onWindowFocusChangedNative(long handle, boolean focused); |
| private native void onSurfaceCreatedNative(long handle, Surface surface); |
| private native void onSurfaceChangedNative(long handle, Surface surface, |
| int format, int width, int height); |
| private native void onSurfaceRedrawNeededNative(long handle, Surface surface); |
| private native void onSurfaceDestroyedNative(long handle); |
| private native void onInputQueueCreatedNative(long handle, long queuePtr); |
| private native void onInputQueueDestroyedNative(long handle, long queuePtr); |
| private native void onContentRectChangedNative(long handle, int x, int y, int w, int h); |
| |
| static class NativeContentView extends View { |
| NativeActivity mActivity; |
| |
| public NativeContentView(Context context) { |
| super(context); |
| } |
| |
| public NativeContentView(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| String libname = "main"; |
| String funcname = "ANativeActivity_onCreate"; |
| ActivityInfo ai; |
| |
| mIMM = getSystemService(InputMethodManager.class); |
| |
| getWindow().takeSurface(this); |
| getWindow().takeInputQueue(this); |
| getWindow().setFormat(PixelFormat.RGB_565); |
| getWindow().setSoftInputMode( |
| WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED |
| | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); |
| |
| mNativeContentView = new NativeContentView(this); |
| mNativeContentView.mActivity = this; |
| setContentView(mNativeContentView); |
| mNativeContentView.requestFocus(); |
| mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); |
| |
| try { |
| ai = getPackageManager().getActivityInfo( |
| getIntent().getComponent(), PackageManager.GET_META_DATA); |
| if (ai.metaData != null) { |
| String ln = ai.metaData.getString(META_DATA_LIB_NAME); |
| if (ln != null) libname = ln; |
| ln = ai.metaData.getString(META_DATA_FUNC_NAME); |
| if (ln != null) funcname = ln; |
| } |
| } catch (PackageManager.NameNotFoundException e) { |
| throw new RuntimeException("Error getting activity info", e); |
| } |
| |
| BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader(); |
| String path = classLoader.findLibrary(libname); |
| |
| if (path == null) { |
| throw new IllegalArgumentException("Unable to find native library " + libname + |
| " using classloader: " + classLoader.toString()); |
| } |
| |
| byte[] nativeSavedState = savedInstanceState != null |
| ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; |
| |
| mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), |
| getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), |
| getAbsolutePath(getExternalFilesDir(null)), |
| Build.VERSION.SDK_INT, getAssets(), nativeSavedState, |
| classLoader, classLoader.getLdLibraryPath()); |
| |
| if (mNativeHandle == 0) { |
| throw new UnsatisfiedLinkError( |
| "Unable to load native library \"" + path + "\": " + getDlError()); |
| } |
| super.onCreate(savedInstanceState); |
| } |
| |
| private static String getAbsolutePath(File file) { |
| return (file != null) ? file.getAbsolutePath() : null; |
| } |
| |
| @Override |
| protected void onDestroy() { |
| mDestroyed = true; |
| if (mCurSurfaceHolder != null) { |
| onSurfaceDestroyedNative(mNativeHandle); |
| mCurSurfaceHolder = null; |
| } |
| if (mCurInputQueue != null) { |
| onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr()); |
| mCurInputQueue = null; |
| } |
| unloadNativeCode(mNativeHandle); |
| super.onDestroy(); |
| } |
| |
| @Override |
| protected void onPause() { |
| super.onPause(); |
| onPauseNative(mNativeHandle); |
| } |
| |
| @Override |
| protected void onResume() { |
| super.onResume(); |
| onResumeNative(mNativeHandle); |
| } |
| |
| @Override |
| protected void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| byte[] state = onSaveInstanceStateNative(mNativeHandle); |
| if (state != null) { |
| outState.putByteArray(KEY_NATIVE_SAVED_STATE, state); |
| } |
| } |
| |
| @Override |
| protected void onStart() { |
| super.onStart(); |
| onStartNative(mNativeHandle); |
| } |
| |
| @Override |
| protected void onStop() { |
| super.onStop(); |
| onStopNative(mNativeHandle); |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration newConfig) { |
| super.onConfigurationChanged(newConfig); |
| if (!mDestroyed) { |
| onConfigurationChangedNative(mNativeHandle); |
| } |
| } |
| |
| @Override |
| public void onLowMemory() { |
| super.onLowMemory(); |
| if (!mDestroyed) { |
| onLowMemoryNative(mNativeHandle); |
| } |
| } |
| |
| @Override |
| public void onWindowFocusChanged(boolean hasFocus) { |
| super.onWindowFocusChanged(hasFocus); |
| if (!mDestroyed) { |
| onWindowFocusChangedNative(mNativeHandle, hasFocus); |
| } |
| } |
| |
| public void surfaceCreated(SurfaceHolder holder) { |
| if (!mDestroyed) { |
| mCurSurfaceHolder = holder; |
| onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); |
| } |
| } |
| |
| public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
| if (!mDestroyed) { |
| mCurSurfaceHolder = holder; |
| onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); |
| } |
| } |
| |
| public void surfaceRedrawNeeded(SurfaceHolder holder) { |
| if (!mDestroyed) { |
| mCurSurfaceHolder = holder; |
| onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); |
| } |
| } |
| |
| public void surfaceDestroyed(SurfaceHolder holder) { |
| mCurSurfaceHolder = null; |
| if (!mDestroyed) { |
| onSurfaceDestroyedNative(mNativeHandle); |
| } |
| } |
| |
| public void onInputQueueCreated(InputQueue queue) { |
| if (!mDestroyed) { |
| mCurInputQueue = queue; |
| onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); |
| } |
| } |
| |
| public void onInputQueueDestroyed(InputQueue queue) { |
| if (!mDestroyed) { |
| onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); |
| mCurInputQueue = null; |
| } |
| } |
| |
| public void onGlobalLayout() { |
| mNativeContentView.getLocationInWindow(mLocation); |
| int w = mNativeContentView.getWidth(); |
| int h = mNativeContentView.getHeight(); |
| if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY |
| || w != mLastContentWidth || h != mLastContentHeight) { |
| mLastContentX = mLocation[0]; |
| mLastContentY = mLocation[1]; |
| mLastContentWidth = w; |
| mLastContentHeight = h; |
| if (!mDestroyed) { |
| onContentRectChangedNative(mNativeHandle, mLastContentX, |
| mLastContentY, mLastContentWidth, mLastContentHeight); |
| } |
| } |
| } |
| |
| @UnsupportedAppUsage |
| void setWindowFlags(int flags, int mask) { |
| getWindow().setFlags(flags, mask); |
| } |
| |
| @UnsupportedAppUsage |
| void setWindowFormat(int format) { |
| getWindow().setFormat(format); |
| } |
| |
| @UnsupportedAppUsage |
| void showIme(int mode) { |
| mIMM.showSoftInput(mNativeContentView, mode); |
| } |
| |
| @UnsupportedAppUsage |
| void hideIme(int mode) { |
| mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); |
| } |
| } |