|  | /* | 
|  | * 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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | 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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | 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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | void setWindowFlags(int flags, int mask) { | 
|  | getWindow().setFlags(flags, mask); | 
|  | } | 
|  |  | 
|  | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | void setWindowFormat(int format) { | 
|  | getWindow().setFormat(format); | 
|  | } | 
|  |  | 
|  | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | void showIme(int mode) { | 
|  | mIMM.showSoftInput(mNativeContentView, mode); | 
|  | } | 
|  |  | 
|  | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | void hideIme(int mode) { | 
|  | mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); | 
|  | } | 
|  | } |