package org.libsdl.app; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.UiModeManager; import android.content.ClipboardManager; import android.content.ClipData; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.InputType; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.view.Display; import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import java.util.Hashtable; import java.util.Locale; /** SDL Activity */ public class SDLActivity { private static final String TAG = "yoyo"; public static boolean mIsResumedCalled, mHasFocus; public static Locale mCurrentLocale; // Handle the state of the native layer public enum NativeState { INIT, RESUMED, PAUSED } public static NativeState mNextNativeState; public static NativeState mCurrentNativeState; // Main components public static SDLActivity mSingleton; /** * This method is called by SDL before loading the native shared libraries. * It can be overridden to provide names of shared libraries to be loaded. * The default implementation returns the defaults. It never returns null. * An array returned by a new implementation must at least contain "SDL2". * Also keep in mind that the order the libraries are loaded may matter. * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). */ public String[] getLibraries() { return new String[] { "hidapi", "SDL2", "FAudioGMS" }; } // Load the .so public void loadLibraries() { for (String lib : getLibraries()) { SDL.loadLibrary(lib); } } public static void initialize() { // The static nature of the singleton and Android quirkyness force us to initialize everything here // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values mSingleton = null; mIsResumedCalled = false; mHasFocus = true; mNextNativeState = NativeState.INIT; mCurrentNativeState = NativeState.INIT; } // Setup public void onCreate(Bundle savedInstanceState) { Log.v(TAG, "Device: " + Build.DEVICE); Log.v(TAG, "Model: " + Build.MODEL); Log.v(TAG, "onCreate()"); try { Thread.currentThread().setName("SDLActivity"); } catch (Exception e) { Log.v(TAG, "modify thread properties failed " + e.toString()); } // Load shared libraries loadLibraries(); // Set up JNI SDL.setupJNI(); // Initialize state SDL.initialize(); // So we can call stuff from static callbacks mSingleton = this; } public void pauseNativeThread() { mNextNativeState = NativeState.PAUSED; mIsResumedCalled = false; SDLActivity.handleNativeState(); } public void resumeNativeThread() { mNextNativeState = NativeState.RESUMED; mIsResumedCalled = true; SDLActivity.handleNativeState(); } // Events public void onPause() { Log.v(TAG, "onPause()"); pauseNativeThread(); } public void onResume() { Log.v(TAG, "onResume()"); resumeNativeThread(); } public void onStop() { Log.v(TAG, "onStop()"); pauseNativeThread(); } public void onStart() { Log.v(TAG, "onStart()"); resumeNativeThread(); } public void onWindowFocusChanged(boolean hasFocus) { Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); mHasFocus = hasFocus; if (hasFocus) { mNextNativeState = NativeState.RESUMED; SDLActivity.handleNativeState(); nativeFocusChanged(true); } else { nativeFocusChanged(false); mNextNativeState = NativeState.PAUSED; SDLActivity.handleNativeState(); } } public void onLowMemory() { Log.v(TAG, "onLowMemory()"); SDLActivity.nativeLowMemory(); } public void onConfigurationChanged(Configuration newConfig) { Log.v(TAG, "onConfigurationChanged()"); if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) { mCurrentLocale = newConfig.locale; SDLActivity.onNativeLocaleChanged(); } } public void onDestroy() { Log.v(TAG, "onDestroy()"); SDLActivity.nativeSendQuit(); SDLActivity.nativeQuit(); } // Called by JNI from SDL. public static void manualBackButton() { } /* Transition to next state */ public static void handleNativeState() { if (mNextNativeState == mCurrentNativeState) { // Already in same state, discard. return; } // Try a transition to init state if (mNextNativeState == NativeState.INIT) { mCurrentNativeState = mNextNativeState; return; } // Try a transition to paused state if (mNextNativeState == NativeState.PAUSED) { nativePause(); mCurrentNativeState = mNextNativeState; Log.i("yoyo", "SDL - PAUSE!"); return; } // Try a transition to resumed state if (mNextNativeState == NativeState.RESUMED) { nativeResume(); mCurrentNativeState = mNextNativeState; Log.i("yoyo", "SDL - RESUME!"); return; } } /** * This method is called by SDL if SDL did not handle a message itself. * This happens if a received message contains an unsupported command. * Method can be overwritten to handle Messages in a different class. * @param command the command of the message. * @param param the parameter of the message. May be null. * @return if the message was handled in overridden method. */ public boolean onUnhandledMessage(int command, Object param) { return false; } /** * A Handler class for Messages from native SDL applications. * It uses current Activities as target (e.g. for the title). * static to prevent implicit references to enclosing object. */ public static class SDLCommandHandler extends Handler { @Override public void handleMessage(Message msg) { } } // Handler for the messages Handler commandHandler = new SDLCommandHandler(); // Send a message from the SDLMain thread boolean sendCommand(int command, Object data) { Message msg = commandHandler.obtainMessage(); msg.arg1 = command; msg.obj = data; boolean result = commandHandler.sendMessage(msg); return result; } // C functions we call public static native int nativeSetupJNI(); public static native int nativeRunMain(String library, String function, Object arguments); public static native void nativeLowMemory(); public static native void nativeSendQuit(); public static native void nativeQuit(); public static native void nativePause(); public static native void nativeResume(); public static native void nativeFocusChanged(boolean hasFocus); public static native void onNativeDropFile(String filename); public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float rate); public static native void onNativeResize(); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native boolean onNativeSoftReturnKey(); public static native void onNativeKeyboardFocusLost(); public static native void onNativeMouse(int button, int action, float x, float y, boolean relative); public static native void onNativeTouch(int touchDevId, int pointerFingerId, int action, float x, float y, float p); public static native void onNativeAccel(float x, float y, float z); public static native void onNativeClipboardChanged(); public static native void onNativeSurfaceCreated(); public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceDestroyed(); public static native String nativeGetHint(String name); public static native void nativeSetenv(String name, String value); public static native void onNativeOrientationChanged(int orientation); public static native void nativeAddTouch(int touchId, String name); public static native void nativePermissionResult(int requestCode, boolean result); public static native void onNativeLocaleChanged(); /** * This method is called by SDL using JNI. */ public static boolean setActivityTitle(String title) { return false; } /** * This method is called by SDL using JNI. */ public static void setWindowStyle(boolean fullscreen) { } /** * This method is called by SDL using JNI. * This is a static method for JNI convenience, it calls a non-static method * so that is can be overridden */ public static void setOrientation(int w, int h, boolean resizable, String hint){ } /** * This can be overridden */ public void setOrientationBis(int w, int h, boolean resizable, String hint){ } /** * This method is called by SDL using JNI. */ public static void minimizeWindow() { } /** * This method is called by SDL using JNI. */ public static boolean shouldMinimizeOnFocusLoss() { return false; } /** * This method is called by SDL using JNI. */ public static boolean isScreenKeyboardShown(){ return false; } /** * This method is called by SDL using JNI. */ public static boolean supportsRelativeMouse(){ return false; } /** * This method is called by SDL using JNI. */ public static boolean setRelativeMouseEnabled(boolean enabled) { return false; } /** * This method is called by SDL using JNI. */ public static boolean sendMessage(int command, int param) { return false; } /** * This method is called by SDL using JNI. */ public static Context getContext() { return SDL.getContext(); } /** * This method is called by SDL using JNI. */ public static boolean isAndroidTV() { return false; } public static double getDiagonal() { return 0.0; } /** * This method is called by SDL using JNI. */ public static boolean isTablet() { return false; } /** * This method is called by SDL using JNI. */ public static boolean isChromebook() { return false; } /** * This method is called by SDL using JNI. */ public static boolean isDeXMode() { return false; } /** * This method is called by SDL using JNI. */ public static DisplayMetrics getDisplayDPI() { return getContext().getResources().getDisplayMetrics(); } /** * This method is called by SDL using JNI. */ public static boolean getManifestEnvironmentVariables() { Log.i("yoyo", "sdl envvars req!"); nativeSetenv("SDL_ANDROID_BLOCK_ON_PAUSE", "1"); nativeSetenv("SDL_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO", "1"); Log.i("yoyo", "sdl envvars ok!"); return true; } /** * This method is called by SDL using JNI. */ public static boolean showTextInput(int x, int y, int w, int h) { return false; } /** * This method is called by SDL using JNI. */ public static Surface getNativeSurface() { return null; } // Input /** * This method is called by SDL using JNI. */ public static void initTouch() { } // Messagebox /** Result of current messagebox. Also used for blocking the calling thread. */ public final int[] messageboxSelection = new int[1]; /** * This method is called by SDL using JNI. * Shows the messagebox from UI thread and block calling thread. * buttonFlags, buttonIds and buttonTexts must have same length. * @param buttonFlags array containing flags for every button. * @param buttonIds array containing id for every button. * @param buttonTexts array containing text for every button. * @param colors null for default or array of length 5 containing colors. * @return button id or -1. */ public int messageboxShowMessageBox( final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors) { return -1; } public void messageboxCreateAndShow(Bundle args) { } private final Runnable rehideSystemUi = new Runnable() { @Override public void run() { } }; public void onSystemUiVisibilityChange(int visibility) { } /** * This method is called by SDL using JNI. */ public static boolean clipboardHasText() { return false; } /** * This method is called by SDL using JNI. */ public static String clipboardGetText() { return ""; } /** * This method is called by SDL using JNI. */ public static void clipboardSetText(String string) { } /** * This method is called by SDL using JNI. */ public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) { return 0; } /** * This method is called by SDL using JNI. */ public static boolean setCustomCursor(int cursorID) { return false; } /** * This method is called by SDL using JNI. */ public static boolean setSystemCursor(int cursorID) { return false; } /** * This method is called by SDL using JNI. */ public static void requestPermission(String permission, int requestCode) { if (Build.VERSION.SDK_INT < 23) { nativePermissionResult(requestCode, true); return; } Activity activity = (Activity)getContext(); if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{permission}, requestCode); } else { nativePermissionResult(requestCode, true); } } public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { boolean result = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED); nativePermissionResult(requestCode, result); } /** * This method is called by SDL using JNI. */ public static int openURL(String url) { return -1; } /** * This method is called by SDL using JNI. */ public static int showToast(String message, int duration, int gravity, int xOffset, int yOffset) { return -1; } } /** Simple runnable to start the SDL application */ class SDLMain implements Runnable { @Override public void run() { try { android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY); } catch (Exception e) { Log.v("SDL", "modify thread properties failed " + e.toString()); } } } class SDLInputConnection extends BaseInputConnection { public SDLInputConnection(View targetView, boolean fullEditor) { super(targetView, fullEditor); } public boolean sendKeyEvent(KeyEvent event) { return false; } public boolean commitText(CharSequence text, int newCursorPosition) { return false; } public boolean setComposingText(CharSequence text, int newCursorPosition) { return false; } public static native void nativeCommitText(String text, int newCursorPosition); public native void nativeGenerateScancodeForUnichar(char c); public native void nativeSetComposingText(String text, int newCursorPosition); public boolean deleteSurroundingText(int beforeLength, int afterLength) { return false; } }