Skip to content

Instantly share code, notes, and snippets.

@ITotalJustice
Last active December 24, 2022 14:28
Show Gist options
  • Save ITotalJustice/4e71d3c47358a5f84425bb6ace828513 to your computer and use it in GitHub Desktop.
Save ITotalJustice/4e71d3c47358a5f84425bb6ace828513 to your computer and use it in GitHub Desktop.
dumping ground for some jni functions i write
#include "native_helper.hpp"
#include <android_native_app_glue.h>
#include <android/log.h>
#include <string>
namespace {
struct JniHelper
{
JniHelper(struct android_app* app)
{
vm = app->activity->vm;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6))
{
if (!vm->AttachCurrentThread(&env, NULL))
{
__android_log_print(ANDROID_LOG_INFO, "JNI-TEST", "attaching thread\n");
thread_attached = true;
}
}
}
~JniHelper()
{
if (thread_attached && vm)
{
vm->DetachCurrentThread();
__android_log_print(ANDROID_LOG_INFO, "JNI-TEST", "detaching thread\n");
}
}
JNIEnv* env{};
JavaVM* vm{};
bool thread_attached{};
};
int getApiLevel(const struct android_app* app)
{
return app->activity->sdkVersion;
}
} // namespace
// https://developer.android.com/reference/android/content/Context#getPackageName()
jstring Android_getPackageName(struct android_app* app)
{
JniHelper jni{app};
const auto ActivityClass = jni.env->GetObjectClass(app->activity->clazz);
const auto getPackageName = jni.env->GetMethodID(ActivityClass, "getPackageName", "()Ljava/lang/String;");
const auto package_name = (jstring)jni.env->CallObjectMethod(app->activity->clazz, getPackageName);
return package_name;
}
// https://developer.android.com/reference/android/os/Environment#isExternalStorageEmulated()
bool Android_isExternalStorageEmulated(struct android_app* app)
{
if (getApiLevel(app) < 11)
{
return false;
}
JniHelper jni{app};
auto Environment = jni.env->FindClass("android/os/Environment");
auto isExternalStorageEmulated = jni.env->GetStaticMethodID(Environment, "isExternalStorageEmulated", "()Z");
const auto result = jni.env->CallStaticBooleanMethod(Environment, isExternalStorageEmulated);
return result > 0;
}
// https://developer.android.com/reference/android/os/Environment#isExternalStorageLegacy()
bool Android_isExternalStorageLegacy(struct android_app* app)
{
if (getApiLevel(app) < 29)
{
return false;
}
JniHelper jni{app};
auto Environment = jni.env->FindClass("android/os/Environment");
auto isExternalStorageLegacy = jni.env->GetStaticMethodID(Environment, "isExternalStorageLegacy", "()Z");
const auto result = jni.env->CallStaticBooleanMethod(Environment, isExternalStorageLegacy);
return result > 0;
}
// https://developer.android.com/reference/android/os/Environment#isExternalStorageManager()
bool Android_isExternalStorageManager(struct android_app* app)
{
if (getApiLevel(app) < 30)
{
return false;
}
JniHelper jni{app};
auto Environment = jni.env->FindClass("android/os/Environment");
auto isExternalStorageManager = jni.env->GetStaticMethodID(Environment, "isExternalStorageManager", "()Z");
const auto result = jni.env->CallStaticBooleanMethod(Environment, isExternalStorageManager);
return result > 0;
}
// https://developer.android.com/reference/android/os/Environment#isExternalStorageRemovable()
bool Android_isExternalStorageRemovable(struct android_app* app)
{
if (getApiLevel(app) < 9)
{
return false;
}
JniHelper jni{app};
auto Environment = jni.env->FindClass("android/os/Environment");
auto isExternalStorageRemovable = jni.env->GetStaticMethodID(Environment, "isExternalStorageRemovable", "()Z");
const auto result = jni.env->CallStaticBooleanMethod(Environment, isExternalStorageRemovable);
return result > 0;
}
// https://developer.android.com/reference/android/provider/Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
// https://developer.android.com/reference/android/provider/Settings#ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
void Android_requestAppAllFilesFilesPermission(struct android_app* app)
{
if (getApiLevel(app) < 30)
{
return;
}
JniHelper jni{app};
// get all the needed classes
const auto SettingsClass = jni.env->FindClass("android/provider/Settings");
const auto ActivityClass = jni.env->FindClass("android/app/Activity");
const auto UriClass = jni.env->FindClass("android/net/Uri");
const auto IntentClass = jni.env->FindClass("android/content/Intent");
// get static string
const auto ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION_id = jni.env->GetStaticFieldID(SettingsClass, "ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION", "Ljava/lang/String;");
const auto ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = (jstring)jni.env->GetStaticObjectField(SettingsClass, ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION_id);
// create uri using the package name
const auto PackageName = Android_getPackageName(app);
const auto PackageNameUTF = jni.env->GetStringUTFChars(PackageName, NULL);
const std::string PackageNameUri = std::string{"package:"} + PackageNameUTF;
jni.env->ReleaseStringUTFChars(PackageName, PackageNameUTF);
// parse and create uri
const auto parse = jni.env->GetStaticMethodID(UriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
const auto PackageUri = jni.env->CallStaticObjectMethod(UriClass, parse, jni.env->NewStringUTF(PackageNameUri.c_str()));
// find constructor and create instance of Intent
const auto IntentClassConstructor = jni.env->GetMethodID(IntentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
const auto IntentObj = jni.env->NewObject(IntentClass, IntentClassConstructor, ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, PackageUri);
// find and call startActivity()
const auto startActivity = jni.env->GetMethodID(ActivityClass, "startActivity", "(Landroid/content/Intent;)V");
jni.env->CallVoidMethod(app->activity->clazz, startActivity, IntentObj);
}
// https://developer.android.com/reference/android/content/Intent#ACTION_GET_CONTENT
// https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT
void Android_file_picker(struct android_app* app)
{
JniHelper jni{app};
// get all the needed classes
const auto ActivityClass = jni.env->FindClass("android/app/Activity");
const auto IntentClass = jni.env->FindClass("android/content/Intent");
// get some constants
const char* action = getApiLevel(app) < 19 ? "ACTION_GET_CONTENT" : "ACTION_OPEN_DOCUMENT";
const auto ACTION_XXX_id = jni.env->GetStaticFieldID(IntentClass, action, "Ljava/lang/String;");
const auto CATEGORY_OPENABLE_id = jni.env->GetStaticFieldID(IntentClass, "CATEGORY_OPENABLE", "Ljava/lang/String;");
const auto ACTION_XXX = (jstring)jni.env->GetStaticObjectField(IntentClass, ACTION_XXX_id);
const auto CATEGORY_OPENABLE = (jstring)jni.env->GetStaticObjectField(IntentClass, CATEGORY_OPENABLE_id);
// find constructor and create instance of Intent
const auto IntentClassConstructor = jni.env->GetMethodID(IntentClass, "<init>", "(Ljava/lang/String;)V");
const auto IntentObj = jni.env->NewObject(IntentClass, IntentClassConstructor, ACTION_XXX);
// need to set a couple of methods
const auto addCategory = jni.env->GetMethodID(IntentClass, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;");
const auto setType = jni.env->GetMethodID(IntentClass, "setType", "(Ljava/lang/String;)Landroid/content/Intent;");
// these return itself so you can chain methods together
jni.env->CallObjectMethod(IntentObj, addCategory, CATEGORY_OPENABLE);
jni.env->CallObjectMethod(IntentObj, setType, jni.env->NewStringUTF("*/*"));
// find and call startActivity() (todo: handle result somehow)
const auto startActivityForResult = jni.env->GetMethodID(ActivityClass, "startActivityForResult", "(Landroid/content/Intent;I)V");
jni.env->CallVoidMethod(app->activity->clazz, startActivityForResult, IntentObj, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment