From 9848610ea26369e65e0db13b6d7d9d2de093cde7 Mon Sep 17 00:00:00 2001 From: BreadFish64 Date: Tue, 15 Jan 2019 19:12:43 -0600 Subject: [PATCH] android: init user path --- src/android/app/src/main/cpp/CMakeLists.txt | 5 ++- src/android/app/src/main/cpp/dummy.cpp | 3 -- .../app/src/main/cpp/native_interface.cpp | 22 +++++++++++ .../app/src/main/cpp/native_interface.h | 16 ++++++++ .../src/main/cpp/ui/main/main_activity.cpp | 15 ++++++++ .../org/citra_emu/citra/CitraApplication.java | 6 ++- .../citra_emu/citra/ui/main/MainActivity.java | 38 +++++++++++++++++++ .../org/citra_emu/citra/utils/FileUtil.java | 19 ++++++++++ .../citra_emu/citra/utils/PermissionUtil.java | 32 ++++++++++++++++ src/common/file_util.cpp | 2 + 10 files changed, 153 insertions(+), 5 deletions(-) delete mode 100644 src/android/app/src/main/cpp/dummy.cpp create mode 100644 src/android/app/src/main/cpp/native_interface.cpp create mode 100644 src/android/app/src/main/cpp/native_interface.h create mode 100644 src/android/app/src/main/cpp/ui/main/main_activity.cpp create mode 100644 src/android/app/src/main/java/org/citra_emu/citra/utils/FileUtil.java create mode 100644 src/android/app/src/main/java/org/citra_emu/citra/utils/PermissionUtil.java diff --git a/src/android/app/src/main/cpp/CMakeLists.txt b/src/android/app/src/main/cpp/CMakeLists.txt index 674b9287c..08db8f4c7 100644 --- a/src/android/app/src/main/cpp/CMakeLists.txt +++ b/src/android/app/src/main/cpp/CMakeLists.txt @@ -1,10 +1,13 @@ cmake_minimum_required(VERSION 3.8) add_library(citra-android SHARED - dummy.cpp + native_interface.cpp + native_interface.h + ui/main/main_activity.cpp ) # find Android's log library find_library(log-lib log) target_link_libraries(citra-android ${log-lib} core common inih) +target_include_directories(citra-android PRIVATE "../../../../../" "./") diff --git a/src/android/app/src/main/cpp/dummy.cpp b/src/android/app/src/main/cpp/dummy.cpp deleted file mode 100644 index d0ef94a09..000000000 --- a/src/android/app/src/main/cpp/dummy.cpp +++ /dev/null @@ -1,3 +0,0 @@ -int dummy(int a, int b) { - return a + b; -} diff --git a/src/android/app/src/main/cpp/native_interface.cpp b/src/android/app/src/main/cpp/native_interface.cpp new file mode 100644 index 000000000..fc4d73b77 --- /dev/null +++ b/src/android/app/src/main/cpp/native_interface.cpp @@ -0,0 +1,22 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "native_interface.h" + +namespace CitraJNI { +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + return JNI_VERSION_1_6; +} + +std::string GetJString(JNIEnv* env, jstring jstr) { + std::string result = ""; + if (!jstr) + return result; + + const char* s = env->GetStringUTFChars(jstr, nullptr); + result = s; + env->ReleaseStringUTFChars(jstr, s); + return result; +} +} // namespace CitraJNI diff --git a/src/android/app/src/main/cpp/native_interface.h b/src/android/app/src/main/cpp/native_interface.h new file mode 100644 index 000000000..a7b99cb51 --- /dev/null +++ b/src/android/app/src/main/cpp/native_interface.h @@ -0,0 +1,16 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace CitraJNI { +extern "C" { +jint JNI_OnLoad(JavaVM* vm, void* reserved); +} + +std::string GetJString(JNIEnv* env, jstring jstr); +} // namespace CitraJNI diff --git a/src/android/app/src/main/cpp/ui/main/main_activity.cpp b/src/android/app/src/main/cpp/ui/main/main_activity.cpp new file mode 100644 index 000000000..412c358a0 --- /dev/null +++ b/src/android/app/src/main/cpp/ui/main/main_activity.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/file_util.h" +#include "native_interface.h" + +namespace MainActivity { +extern "C" { +JNICALL void Java_org_citra_1emu_citra_ui_main_MainActivity_initUserPath(JNIEnv* env, jclass type, + jstring path) { + FileUtil::SetUserPath(CitraJNI::GetJString(env, path)); +} +}; +}; // namespace MainActivity diff --git a/src/android/app/src/main/java/org/citra_emu/citra/CitraApplication.java b/src/android/app/src/main/java/org/citra_emu/citra/CitraApplication.java index 07a782853..10cb52783 100644 --- a/src/android/app/src/main/java/org/citra_emu/citra/CitraApplication.java +++ b/src/android/app/src/main/java/org/citra_emu/citra/CitraApplication.java @@ -6,4 +6,8 @@ package org.citra_emu.citra; import android.app.Application; -public class CitraApplication extends Application {} +public class CitraApplication extends Application { + static { + System.loadLibrary("citra-android"); + } +} diff --git a/src/android/app/src/main/java/org/citra_emu/citra/ui/main/MainActivity.java b/src/android/app/src/main/java/org/citra_emu/citra/ui/main/MainActivity.java index 97b36f0c1..0ae764798 100644 --- a/src/android/app/src/main/java/org/citra_emu/citra/ui/main/MainActivity.java +++ b/src/android/app/src/main/java/org/citra_emu/citra/ui/main/MainActivity.java @@ -4,15 +4,53 @@ package org.citra_emu.citra.ui.main; +import android.Manifest; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import org.citra_emu.citra.R; +import org.citra_emu.citra.utils.FileUtil; +import org.citra_emu.citra.utils.PermissionUtil; public final class MainActivity extends AppCompatActivity { + + // Java enums suck + private interface PermissionCodes { int INIT_USER_PATH = 0; } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + + PermissionUtil.verifyPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, + PermissionCodes.INIT_USER_PATH); } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + switch (requestCode) { + case PermissionCodes.INIT_USER_PATH: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + initUserPath(FileUtil.getUserPath().toString()); + } else { + AlertDialog.Builder dialog = + new AlertDialog.Builder(this) + .setTitle("Permission Error") + .setMessage("Citra requires storage permissions to function.") + .setCancelable(false) + .setPositiveButton("OK", (dialogInterface, which) -> { + PermissionUtil.verifyPermission( + MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE, + PermissionCodes.INIT_USER_PATH); + }); + dialog.show(); + } + } + } + + private static native void initUserPath(String path); } diff --git a/src/android/app/src/main/java/org/citra_emu/citra/utils/FileUtil.java b/src/android/app/src/main/java/org/citra_emu/citra/utils/FileUtil.java new file mode 100644 index 000000000..5346c5352 --- /dev/null +++ b/src/android/app/src/main/java/org/citra_emu/citra/utils/FileUtil.java @@ -0,0 +1,19 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package org.citra_emu.citra.utils; + +import android.os.Environment; + +import java.io.File; + +public class FileUtil { + public static File getUserPath() { + File storage = Environment.getExternalStorageDirectory(); + File userPath = new File(storage, "citra"); + if (!userPath.isDirectory()) + userPath.mkdir(); + return userPath; + } +} diff --git a/src/android/app/src/main/java/org/citra_emu/citra/utils/PermissionUtil.java b/src/android/app/src/main/java/org/citra_emu/citra/utils/PermissionUtil.java new file mode 100644 index 000000000..33c8129e5 --- /dev/null +++ b/src/android/app/src/main/java/org/citra_emu/citra/utils/PermissionUtil.java @@ -0,0 +1,32 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package org.citra_emu.citra.utils; + +import android.app.Activity; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +public class PermissionUtil { + + /** + * Checks a permission, if needed shows a dialog to request it + * + * @param activity the activity requiring the permission + * @param permission the permission needed + * @param requestCode supplied to the callback to determine the next action + */ + public static void verifyPermission(Activity activity, String permission, int requestCode) { + if (ContextCompat.checkSelfPermission(activity, permission) == + PackageManager.PERMISSION_GRANTED) { + // call the callback called by requestPermissions + activity.onRequestPermissionsResult(requestCode, new String[] {permission}, + new int[] {PackageManager.PERMISSION_GRANTED}); + return; + } + + ActivityCompat.requestPermissions(activity, new String[] {permission}, requestCode); + } +} diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 52288847a..e5f8eb899 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -695,6 +695,8 @@ void SetUserPath(const std::string& path) { g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); +#elif ANDROID + ASSERT_MSG(false, "Specified path {} is not valid", path); #else if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;