From 4f5bd0a262c3a8be1a01dd3372644f15b85321ca Mon Sep 17 00:00:00 2001 From: Robert Kirkman Date: Wed, 12 Feb 2025 20:18:13 -0600 Subject: [PATCH] special external storage permission - bump SDL to 2.32.0 - bump NDK to r27c - bump AGP to 8.8.0 - bump Gradle to 8.10.2 - bump CMake to 3.31.5 - bump targetSdkVersion to 34 - add GitHub Actions Release support (Debug) - add Special External Storage Permission support - fix "oot.otr not found" error on first launch - drop Android 4 support --- .github/workflows/generate-release.yml | 22 ++ .gitignore | 3 + .gitmodules | 2 +- app/build.gradle | 51 ++-- app/jni/src/libultraship | 2 +- app/jni/src/soh/soh/Extractor/Extract.cpp | 76 +++--- app/src/main/AndroidManifest.xml | 7 +- .../java/com/dishii/soh/AssetCopyUtil.java | 103 ++++---- .../java/com/dishii/soh/MainActivity.java | 222 ++++++++++-------- .../java/org/libsdl/app/HIDDeviceManager.java | 18 +- app/src/main/java/org/libsdl/app/SDL.java | 14 +- .../main/java/org/libsdl/app/SDLActivity.java | 55 ++--- .../org/libsdl/app/SDLControllerManager.java | 16 +- build.gradle | 2 +- gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 6 +- 16 files changed, 336 insertions(+), 265 deletions(-) create mode 100644 .github/workflows/generate-release.yml diff --git a/.github/workflows/generate-release.yml b/.github/workflows/generate-release.yml new file mode 100644 index 0000000..58f26c1 --- /dev/null +++ b/.github/workflows/generate-release.yml @@ -0,0 +1,22 @@ +name: generate-release +on: + workflow_dispatch: +jobs: + build-android: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install dependencies + run: sudo apt-get install -y ninja-build + - name: Build SoH + run: | + ./gradlew assembleDebug + mv app/build/outputs/apk/debug/app-debug.apk soh.apk + - name: Create release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ github.ref }} + file: soh.apk diff --git a/.gitignore b/.gitignore index 2dc11ba..11977d5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ google-services.json # Android Profiling *.hprof + +# vscode +.vscode diff --git a/.gitmodules b/.gitmodules index 6fbcd88..ca98791 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "app/jni/src/libultraship"] path = app/jni/src/libultraship - url = https://github.com/Waterdish/libultraship.git + url = https://github.com/robertkirkman/libultraship.git diff --git a/app/build.gradle b/app/build.gradle index feadf82..d72b4e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,4 @@ -def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY'); +def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY') def buildAsApplication = !buildAsLibrary if (buildAsApplication) { apply plugin: 'com.android.application' @@ -8,25 +8,21 @@ else { } android { - ndkPath "/home/waterdish/Android/Sdk/ndk/26.0.10792818" // Point to your own NDK - compileSdkVersion 31 + ndkVersion '27.2.12479018' + compileSdkVersion 34 defaultConfig { if (buildAsApplication) { applicationId "com.dishii.soh" } - minSdkVersion 18 - targetSdkVersion 31 + minSdkVersion 21 + //noinspection OldTargetApi + targetSdkVersion 34 versionCode 6 versionName "1.3.0" externalNativeBuild { - //ndkBuild { - // arguments "APP_PLATFORM=android-23" - // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - //} cmake { - arguments "-DANDROID_APP_PLATFORM=android-23", "-DANDROID_STL=c++_static", "-DHAVE_LD_VERSION_SCRIPT=OFF",'-DUSE_OPENGLES=ON' + arguments "-DANDROID_APPNAME=${applicationId}", "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DHAVE_LD_VERSION_SCRIPT=OFF", "-DUSE_OPENGLES=ON" abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - //abiFilters 'arm64-v8a' } } } @@ -36,7 +32,14 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - applicationVariants.all { variant -> + buildFeatures { + buildConfig = true + } + namespace 'com.dishii.soh' + lint { + abortOnError false + } + applicationVariants.configureEach { variant -> tasks["merge${variant.name.capitalize()}Assets"] .dependsOn("externalNativeBuild${variant.name.capitalize()}") } @@ -45,27 +48,21 @@ android { jniLibs.srcDir 'libs' } externalNativeBuild { - //ndkBuild { - // path 'jni/Android.mk' - //} cmake { path 'jni/CMakeLists.txt' - version "3.25.1" + version "3.31.5" } } } - lintOptions { - abortOnError false - } - + if (buildAsLibrary) { libraryVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith(".aar")) { - def fileName = "org.libsdl.app.aar"; - output.outputFile = new File(outputFile.parent, fileName); + def fileName = "com.dishii.soh.aar" + output.outputFile = new File(outputFile.parent, fileName) } } } @@ -74,13 +71,13 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.core:core:1.7.0' // Use the latest version - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.core:core:1.13.1' + implementation 'androidx.constraintlayout:constraintlayout:2.2.0' } -task wrapper(type: Wrapper) { - gradleVersion = '7.3' +tasks.register('wrapper', Wrapper) { + gradleVersion = '8.10.2' } -task prepareKotlinBuildScriptModel { +tasks.register('prepareKotlinBuildScriptModel') { } diff --git a/app/jni/src/libultraship b/app/jni/src/libultraship index 03e683e..94f648a 160000 --- a/app/jni/src/libultraship +++ b/app/jni/src/libultraship @@ -1 +1 @@ -Subproject commit 03e683e1d1be96ed714f0459f603e40ea1e82444 +Subproject commit 94f648a98eefd65d3d4617a30ebc761cb80fe0e0 diff --git a/app/jni/src/soh/soh/Extractor/Extract.cpp b/app/jni/src/soh/soh/Extractor/Extract.cpp index db52580..f3fdd34 100644 --- a/app/jni/src/soh/soh/Extractor/Extract.cpp +++ b/app/jni/src/soh/soh/Extractor/Extract.cpp @@ -95,7 +95,7 @@ enum class ButtonId : int { }; #ifdef __ANDROID__ -const char* javaRomPath = NULL; +static char javaRomPath[4096] = { 0 }; bool fileDialogOpen = false; //function to be called from C @@ -108,11 +108,28 @@ void openFilePickerFromC(JNIEnv* env, jobject javaObject) { // Define the native method to handle the selected file path extern "C" void JNICALL Java_com_dishii_soh_MainActivity_nativeHandleSelectedFile(JNIEnv* env, jobject obj, jstring filePath) { const char* filePathStr = env->GetStringUTFChars(filePath, 0); - javaRomPath = strdup(filePathStr); // save filepath to string + snprintf(javaRomPath, sizeof(javaRomPath), "%s", filePathStr); fileDialogOpen = false; env->ReleaseStringUTFChars(filePath, filePathStr); } +/* + * (MESSY) begin duplicated code from LUS::Context::GetAppDirectoryPath() __ANDROID__ implementation + */ +std::string getExternalAssetsPathFromCPP(JNIEnv* javaEnv, jobject javaObject) { + jclass javaClass = javaEnv->GetObjectClass(javaObject); + jmethodID getExternalAssetsPathMethod = javaEnv->GetMethodID(javaClass, "getExternalAssetsPath", "()Ljava/lang/String;"); + jstring externalAssetsPath_jstr = static_cast(javaEnv->CallObjectMethod(javaObject, getExternalAssetsPathMethod)); + + const char *externalAssetsPath_cstr = javaEnv->GetStringUTFChars(externalAssetsPath_jstr, nullptr); + std::string externalAssetsPath(externalAssetsPath_cstr); + + javaEnv->ReleaseStringUTFChars(externalAssetsPath_jstr, externalAssetsPath_cstr); + return externalAssetsPath; +} +/* + * (MESSY) end duplicated code + */ #endif void Extractor::ShowErrorBox(const char* title, const char* text) { @@ -248,7 +265,7 @@ void Extractor::GetRoms(std::vector& roms) { // if (h != nullptr) { // CloseHandle(h); //} -#elif unix && !defined(__ANDROID__) +#elif unix // Open the directory of the app. DIR* d = opendir(mSearchPath.c_str()); struct dirent* dir; @@ -272,33 +289,6 @@ void Extractor::GetRoms(std::vector& roms) { } } closedir(d); -#elif defined(__ANDROID__) - const char* androidAssetPath = SDL_AndroidGetExternalStoragePath(); - if (androidAssetPath == NULL) { - printf("Error accessing Android assets directory: %s\n", SDL_GetError()); - return; - } - - // Use androidAssetPath for file operations - // Example: List files in the directory - DIR* dir; - struct dirent* entry; - - if ((dir = opendir(androidAssetPath)) != NULL) { - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_REG) { - char* filename = entry->d_name; - // Check file extension and process accordingly - if (strstr(filename, ".n64") || strstr(filename, ".z64") || strstr(filename, ".v64")) { - std::string fullPath = std::string(androidAssetPath) + "/" + filename; - roms.push_back(fullPath); - } - } - } - closedir(dir); - } else { - printf("Error opening directory: %s\n", androidAssetPath); - } #else for (const auto& file : std::filesystem::directory_iterator(mSearchPath)) { if (file.is_directory()) @@ -358,29 +348,18 @@ bool Extractor::GetRomPathFromBox() { //Do nothing until a file is chosen SDL_Delay(250); } - SDL_Log("%s",javaRomPath); + SDL_Log("javaRomPath: %s", javaRomPath); + selection.push_back(javaRomPath); - - if (selection.empty()) { - return false; - } - - mCurrentRomPath = selection[0]; - - if (javaRomPath) { - free((void*)javaRomPath); - javaRomPath = NULL; - } - #else auto selection = pfd::open_file("Select a file", mSearchPath, { "N64 Roms", "*.z64 *.n64 *.v64" }).result(); +#endif if (selection.empty()) { return false; } mCurrentRomPath = selection[0]; -#endif mCurRomSize = GetCurRomSize(); return true; } @@ -614,10 +593,13 @@ const char* Extractor::GetZapdVerStr() const { } std::string Extractor::Mkdtemp() { -#ifndef __ANDROID__ - std::string temp_dir = std::filesystem::temp_directory_path().string(); +#ifdef __ANDROID__ + JNIEnv* javaEnv = (JNIEnv*)SDL_AndroidGetJNIEnv(); + jobject javaObject = (jobject)SDL_AndroidGetActivity(); + std::string temp_dir = getExternalAssetsPathFromCPP(javaEnv, javaObject) + "/tmp"; + std::filesystem::create_directories(temp_dir); #else - std::string temp_dir = SDL_AndroidGetExternalStoragePath(); + std::string temp_dir = std::filesystem::temp_directory_path().string(); #endif // create 6 random alphanumeric characters diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a9ad2c..e88f14e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,6 @@ com.gamemaker.game --> @@ -54,8 +53,8 @@ - + @@ -73,7 +72,8 @@ android:allowBackup="true" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:hardwareAccelerated="true" - android:appCategory="game" > + android:appCategory="game" + android:requestLegacyExternalStorage="true">