|
28 | 28 | #include <sys/types.h> |
29 | 29 | #endif |
30 | 30 |
|
| 31 | +#ifdef __ANDROID__ |
| 32 | +#include <SDL_system.h> |
| 33 | +#include <SDL.h> |
| 34 | +#include <jni.h> |
| 35 | +#include "options/Option.h" |
| 36 | +#endif |
| 37 | + |
31 | 38 | namespace |
32 | 39 | { |
33 | 40 | const char* ORGANIZATION_NAME = "HardLightProductions"; |
@@ -840,3 +847,126 @@ SCP_string os_get_config_path(const SCP_string& subpath) |
840 | 847 | return ss.str(); |
841 | 848 | } |
842 | 849 |
|
| 850 | +/* |
| 851 | + Special functions for Android |
| 852 | +*/ |
| 853 | + |
| 854 | +#ifdef __ANDROID__ |
| 855 | +// Helper to get a static method from a java class and clear the exception |
| 856 | +// if the method is not found. This is needed to avoid crashing on the next JNI request. |
| 857 | +static jmethodID android_get_static_method(JNIEnv* e, jclass cls, const char* name, const char* sig) |
| 858 | +{ |
| 859 | + jmethodID m = e->GetStaticMethodID(cls, name, sig); |
| 860 | + if (e->ExceptionCheck()) { |
| 861 | + e->ExceptionClear(); |
| 862 | + m = nullptr; |
| 863 | + mprintf(("OSAPI : Method: %s. Not found on GameActivity! Signature: %s. \n", name, sig)); |
| 864 | + } |
| 865 | + return m; |
| 866 | +} |
| 867 | + |
| 868 | +SCP_string os_get_working_folder_path() |
| 869 | +{ |
| 870 | + SCP_string wfp{}; |
| 871 | + |
| 872 | + //Get the JNI Environment pointer and current Activity instance via SDL |
| 873 | + JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); |
| 874 | + jobject activity = (jobject)SDL_AndroidGetActivity(); |
| 875 | + |
| 876 | + if (env && activity) { |
| 877 | + // Locate the Java class (GameActivity on KnossosNET) |
| 878 | + jclass ga = env->GetObjectClass(activity); |
| 879 | + if(ga) { |
| 880 | + // Get the methodID (activity, methodName, signature); |
| 881 | + // "()Ljava/lang/String;" means it takes no paramenters and returns a string |
| 882 | + jmethodID methodId = android_get_static_method (env, ga, "getWorkingFolder", "()Ljava/lang/String;"); |
| 883 | + if (methodId) { |
| 884 | + jstring jString = (jstring)env->CallStaticObjectMethod(ga, methodId); |
| 885 | + if (jString) { |
| 886 | + const char* workingFolder = env->GetStringUTFChars(jString, 0); |
| 887 | + wfp = SCP_string(workingFolder); |
| 888 | + env->ReleaseStringUTFChars(jString, workingFolder); |
| 889 | + env->DeleteLocalRef(jString); |
| 890 | + } else { |
| 891 | + mprintf(("os_get_working_folder_path: Couldn't get the jString.\n")); |
| 892 | + } |
| 893 | + } else { |
| 894 | + mprintf(("os_get_working_folder_path: Couldn't get the methodID.\n")); |
| 895 | + } |
| 896 | + env->DeleteLocalRef(ga); |
| 897 | + } else { |
| 898 | + mprintf(("os_get_working_folder_path: Couldn't get java class.\n")); |
| 899 | + } |
| 900 | + } else { |
| 901 | + mprintf(("os_get_working_folder_path: Couldn't get JNI enviroment or activity.\n")); |
| 902 | + } |
| 903 | + |
| 904 | + if (wfp.empty()) { |
| 905 | + mprintf(("Couldn't get working folder path from Java class, reverting to SDL default.\n")); |
| 906 | + // Fallback to app space on internal storage |
| 907 | + const char* fallbackPath = SDL_AndroidGetExternalStoragePath(); |
| 908 | + if (fallbackPath) { |
| 909 | + wfp = SCP_string(fallbackPath); |
| 910 | + wfp += "/files/"; |
| 911 | + } |
| 912 | + } |
| 913 | + |
| 914 | + // Ensure path ends with a dir separator |
| 915 | + if (!wfp.empty() && (wfp.back() != DIR_SEPARATOR_CHAR)) { |
| 916 | + wfp += DIR_SEPARATOR_CHAR; |
| 917 | + } |
| 918 | + mprintf(("Using working folder: %s\n", wfp.c_str())); |
| 919 | + return wfp; |
| 920 | +} |
| 921 | + |
| 922 | +void os_touch_overlay_toggle(bool status) |
| 923 | +{ |
| 924 | + //Get the JNI Environment pointer and current Activity instance via SDL |
| 925 | + JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); |
| 926 | + jobject activity = (jobject)SDL_AndroidGetActivity(); |
| 927 | + |
| 928 | + if (env && activity) { |
| 929 | + // Locate the Java class (GameActivity on KnossosNET) |
| 930 | + jclass ga = env->GetObjectClass(activity); |
| 931 | + if(ga) { |
| 932 | + jmethodID methodId = android_get_static_method (env, ga, status ? "enableOverlay" : "disableOverlay", "()V"); |
| 933 | + if (methodId) { |
| 934 | + env->CallStaticVoidMethod(ga, methodId); |
| 935 | + } else { |
| 936 | + mprintf(("os_touch_overlay_toggle: Couldn't get the methodID.\n")); |
| 937 | + } |
| 938 | + env->DeleteLocalRef(ga); |
| 939 | + } else { |
| 940 | + mprintf(("os_touch_overlay_toggle: Couldn't get java class.\n")); |
| 941 | + } |
| 942 | + } else { |
| 943 | + mprintf(("os_touch_overlay_toggle: Couldn't get JNI enviroment or activity.\n")); |
| 944 | + } |
| 945 | +} |
| 946 | + |
| 947 | +static bool touch_ui_change(bool new_val, bool initial) |
| 948 | +{ |
| 949 | + if (initial) { |
| 950 | + return false; |
| 951 | + } |
| 952 | + os_touch_overlay_toggle(new_val); |
| 953 | + return true; |
| 954 | +} |
| 955 | + |
| 956 | +static auto TouchOverlayOption = options::OptionBuilder<bool>("Input.TouchOverlay", |
| 957 | + std::pair<const char*, int>{"Touch Overlay", -1}, |
| 958 | + std::pair<const char*, int>{"Enable or disable the Touch Overlay", -1}) |
| 959 | + .category(std::make_pair("Input", 1827)) |
| 960 | + .level(options::ExpertLevel::Beginner) |
| 961 | + .change_listener(touch_ui_change) |
| 962 | + .default_val(true) |
| 963 | + .importance(0) |
| 964 | + .finish(); |
| 965 | + |
| 966 | +void os_touch_overlay_init() |
| 967 | +{ |
| 968 | + os_touch_overlay_toggle(TouchOverlayOption->getValue()); |
| 969 | +} |
| 970 | + |
| 971 | +#endif |
| 972 | + |
0 commit comments