[Base] Android JNIEnv attachment and LaunchWebBrowser
This commit is contained in:
parent
54b057a46b
commit
3f817fb241
|
@ -24,7 +24,7 @@ extern "C" int main(int argc, char** argv) {
|
|||
|
||||
// Initialize Android globals, including logging. Needs parsed cvars.
|
||||
// TODO(Triang3l): Obtain the actual API level.
|
||||
xe::InitializeAndroidAppFromMainThread(__ANDROID_API__);
|
||||
xe::InitializeAndroidAppFromMainThread(__ANDROID_API__, nullptr, nullptr);
|
||||
|
||||
std::vector<std::string> args;
|
||||
for (int n = 0; n < argc; n++) {
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
|
||||
#include "xenia/base/main_android.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <pthread.h>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/system.h"
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -22,7 +26,25 @@ static size_t android_initializations_ = 0;
|
|||
|
||||
static int32_t android_api_level_ = __ANDROID_API__;
|
||||
|
||||
void InitializeAndroidAppFromMainThread(int32_t api_level) {
|
||||
static JNIEnv* android_main_thread_jni_env_ = nullptr;
|
||||
static JavaVM* android_java_vm_ = nullptr;
|
||||
static pthread_key_t android_thread_jni_env_key_;
|
||||
static jobject android_application_context_ = nullptr;
|
||||
|
||||
static void AndroidThreadJNIEnvDestructor(void* jni_env_pointer) {
|
||||
// The JNIEnv pointer for the main thread is taken externally, the lifetime of
|
||||
// the attachment is not managed by the key.
|
||||
JNIEnv* jni_env = static_cast<JNIEnv*>(jni_env_pointer);
|
||||
if (jni_env && jni_env != android_main_thread_jni_env_) {
|
||||
android_java_vm_->DetachCurrentThread();
|
||||
}
|
||||
// Multiple iterations of destructor invocations can be done - clear.
|
||||
pthread_setspecific(android_thread_jni_env_key_, nullptr);
|
||||
}
|
||||
|
||||
void InitializeAndroidAppFromMainThread(int32_t api_level,
|
||||
JNIEnv* main_thread_jni_env,
|
||||
jobject application_context) {
|
||||
if (android_initializations_++) {
|
||||
// Already initialized for another component in the process.
|
||||
return;
|
||||
|
@ -32,6 +54,45 @@ void InitializeAndroidAppFromMainThread(int32_t api_level) {
|
|||
// subsystem initialization itself.
|
||||
android_api_level_ = api_level;
|
||||
|
||||
android_main_thread_jni_env_ = main_thread_jni_env;
|
||||
if (main_thread_jni_env) {
|
||||
// In a Java VM, not just in a process that runs an executable - set up
|
||||
// the attachment of threads to the Java VM.
|
||||
if (main_thread_jni_env->GetJavaVM(&android_java_vm_) < 0) {
|
||||
// Logging has not been initialized yet.
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread",
|
||||
"Failed to get the Java VM from the JNI environment of the main "
|
||||
"thread");
|
||||
std::abort();
|
||||
}
|
||||
if (pthread_key_create(&android_thread_jni_env_key_,
|
||||
AndroidThreadJNIEnvDestructor)) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread",
|
||||
"Failed to create the thread-specific JNI environment key");
|
||||
std::abort();
|
||||
}
|
||||
if (pthread_setspecific(android_thread_jni_env_key_, main_thread_jni_env)) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread",
|
||||
"Failed to set the thread-specific JNI environment pointer for the "
|
||||
"main thread");
|
||||
std::abort();
|
||||
}
|
||||
if (application_context) {
|
||||
android_application_context_ =
|
||||
main_thread_jni_env->NewGlobalRef(application_context);
|
||||
if (!android_application_context_) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread",
|
||||
"Failed to create a global reference to the application context "
|
||||
"object");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Logging uses threading.
|
||||
xe::threading::AndroidInitialize();
|
||||
|
||||
|
@ -40,6 +101,15 @@ void InitializeAndroidAppFromMainThread(int32_t api_level) {
|
|||
xe::InitializeLogging("xenia");
|
||||
|
||||
xe::memory::AndroidInitialize();
|
||||
|
||||
if (android_application_context_) {
|
||||
if (!xe::InitializeAndroidSystemForApplicationContext()) {
|
||||
__android_log_write(ANDROID_LOG_ERROR,
|
||||
"InitializeAndroidAppFromMainThread",
|
||||
"Failed to initialize system UI interaction");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownAndroidAppFromMainThread() {
|
||||
|
@ -52,15 +122,36 @@ void ShutdownAndroidAppFromMainThread() {
|
|||
return;
|
||||
}
|
||||
|
||||
xe::ShutdownAndroidSystem();
|
||||
|
||||
xe::memory::AndroidShutdown();
|
||||
|
||||
xe::ShutdownLogging();
|
||||
|
||||
xe::threading::AndroidShutdown();
|
||||
|
||||
if (android_application_context_) {
|
||||
android_main_thread_jni_env_->DeleteGlobalRef(android_application_context_);
|
||||
android_application_context_ = nullptr;
|
||||
}
|
||||
if (android_java_vm_) {
|
||||
android_java_vm_ = nullptr;
|
||||
pthread_key_delete(android_thread_jni_env_key_);
|
||||
}
|
||||
android_main_thread_jni_env_ = nullptr;
|
||||
|
||||
android_api_level_ = __ANDROID_API__;
|
||||
}
|
||||
|
||||
int32_t GetAndroidApiLevel() { return android_api_level_; }
|
||||
|
||||
JNIEnv* GetAndroidThreadJNIEnv() {
|
||||
if (!android_java_vm_) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<JNIEnv*>(pthread_getspecific(android_thread_jni_env_key_));
|
||||
}
|
||||
|
||||
jobject GetAndroidApplicationContext() { return android_application_context_; }
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -10,6 +10,7 @@
|
|||
#ifndef XENIA_BASE_MAIN_ANDROID_H_
|
||||
#define XENIA_BASE_MAIN_ANDROID_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
|
@ -27,14 +28,22 @@ namespace xe {
|
|||
// counting internally.
|
||||
//
|
||||
// In standalone console apps built with $(BUILD_EXECUTABLE), these functions
|
||||
// must be called in `main`.
|
||||
void InitializeAndroidAppFromMainThread(int32_t api_level);
|
||||
// must be called in `main`, with a null main thread JNI environment.
|
||||
void InitializeAndroidAppFromMainThread(int32_t api_level,
|
||||
JNIEnv* main_thread_jni_env,
|
||||
jobject application_context);
|
||||
void ShutdownAndroidAppFromMainThread();
|
||||
|
||||
// May be the minimum supported level if the initialization was done without a
|
||||
// configuration.
|
||||
int32_t GetAndroidApiLevel();
|
||||
|
||||
// May return null if not in a Java VM process, or in case of a failure to
|
||||
// attach on a non-main thread.
|
||||
JNIEnv* GetAndroidThreadJNIEnv();
|
||||
// Returns the global reference if in an application context, or null otherwise.
|
||||
jobject GetAndroidApplicationContext();
|
||||
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_BASE_MAIN_ANDROID_H_
|
||||
|
|
|
@ -13,10 +13,17 @@
|
|||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
#if XE_PLATFORM_ANDROID
|
||||
bool InitializeAndroidSystemForApplicationContext();
|
||||
void ShutdownAndroidSystem();
|
||||
#endif
|
||||
|
||||
// The URL must include the protocol.
|
||||
void LaunchWebBrowser(const std::string_view url);
|
||||
void LaunchFileExplorer(const std::filesystem::path& path);
|
||||
|
||||
|
|
|
@ -7,17 +7,285 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/main_android.h"
|
||||
#include "xenia/base/system.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// To store jmethodIDs persistently, global references to the classes are
|
||||
// required to prevent the classes from being unloaded and reloaded, potentially
|
||||
// changing the method IDs.
|
||||
|
||||
static jclass android_system_application_context_class_ = nullptr;
|
||||
static jmethodID android_system_application_context_start_activity_ = nullptr;
|
||||
|
||||
static jclass android_system_uri_class_ = nullptr;
|
||||
static jmethodID android_system_uri_parse_ = nullptr;
|
||||
|
||||
static jclass android_system_intent_class_ = nullptr;
|
||||
static jfieldID android_system_intent_action_view_field_id_ = nullptr;
|
||||
static jfieldID android_system_intent_flag_activity_new_task_field_id_ =
|
||||
nullptr;
|
||||
static jmethodID android_system_intent_init_action_uri_ = nullptr;
|
||||
static jmethodID android_system_intent_add_flags_ = nullptr;
|
||||
static jobject android_system_intent_action_view_ = nullptr;
|
||||
static jint android_system_intent_flag_activity_new_task_;
|
||||
|
||||
static bool android_system_initialized_ = false;
|
||||
|
||||
bool InitializeAndroidSystemForApplicationContext() {
|
||||
assert_false(android_system_initialized_);
|
||||
|
||||
JNIEnv* jni_env = GetAndroidThreadJNIEnv();
|
||||
if (!jni_env) {
|
||||
return false;
|
||||
}
|
||||
jobject application_context = xe::GetAndroidApplicationContext();
|
||||
if (!application_context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Application context.
|
||||
{
|
||||
{
|
||||
jclass application_context_class_local_ref =
|
||||
jni_env->GetObjectClass(application_context);
|
||||
if (!application_context_class_local_ref) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to get the "
|
||||
"class of the application context");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
android_system_application_context_class_ =
|
||||
reinterpret_cast<jclass>(jni_env->NewGlobalRef(
|
||||
reinterpret_cast<jobject>(application_context_class_local_ref)));
|
||||
jni_env->DeleteLocalRef(application_context_class_local_ref);
|
||||
}
|
||||
if (!android_system_application_context_class_) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to create a "
|
||||
"global reference to the class of the application context");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
bool application_context_ids_obtained = true;
|
||||
application_context_ids_obtained &=
|
||||
(android_system_application_context_start_activity_ =
|
||||
jni_env->GetMethodID(android_system_application_context_class_,
|
||||
"startActivity",
|
||||
"(Landroid/content/Intent;)V")) != nullptr;
|
||||
if (!application_context_ids_obtained) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to get the "
|
||||
"application context class IDs");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// URI.
|
||||
{
|
||||
{
|
||||
jclass uri_class_local_ref = jni_env->FindClass("android/net/Uri");
|
||||
if (!uri_class_local_ref) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to find the "
|
||||
"URI class");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
android_system_uri_class_ =
|
||||
reinterpret_cast<jclass>(jni_env->NewGlobalRef(
|
||||
reinterpret_cast<jobject>(uri_class_local_ref)));
|
||||
jni_env->DeleteLocalRef(uri_class_local_ref);
|
||||
}
|
||||
if (!android_system_uri_class_) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to create a "
|
||||
"global reference to the URI class");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
bool uri_ids_obtained = true;
|
||||
uri_ids_obtained &=
|
||||
(android_system_uri_parse_ = jni_env->GetStaticMethodID(
|
||||
android_system_uri_class_, "parse",
|
||||
"(Ljava/lang/String;)Landroid/net/Uri;")) != nullptr;
|
||||
if (!uri_ids_obtained) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to get the URI "
|
||||
"class IDs");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Intent.
|
||||
{
|
||||
{
|
||||
jclass intent_class_local_ref =
|
||||
jni_env->FindClass("android/content/Intent");
|
||||
if (!intent_class_local_ref) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to find the "
|
||||
"intent class");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
android_system_intent_class_ =
|
||||
reinterpret_cast<jclass>(jni_env->NewGlobalRef(
|
||||
reinterpret_cast<jobject>(intent_class_local_ref)));
|
||||
jni_env->DeleteLocalRef(intent_class_local_ref);
|
||||
}
|
||||
if (!android_system_intent_class_) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to create a "
|
||||
"global reference to the intent class");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
bool intent_ids_obtained = true;
|
||||
intent_ids_obtained &= (android_system_intent_action_view_field_id_ =
|
||||
jni_env->GetStaticFieldID(
|
||||
android_system_intent_class_, "ACTION_VIEW",
|
||||
"Ljava/lang/String;")) != nullptr;
|
||||
intent_ids_obtained &=
|
||||
(android_system_intent_flag_activity_new_task_field_id_ =
|
||||
jni_env->GetStaticFieldID(android_system_intent_class_,
|
||||
"FLAG_ACTIVITY_NEW_TASK", "I")) !=
|
||||
nullptr;
|
||||
intent_ids_obtained &=
|
||||
(android_system_intent_init_action_uri_ = jni_env->GetMethodID(
|
||||
android_system_intent_class_, "<init>",
|
||||
"(Ljava/lang/String;Landroid/net/Uri;)V")) != nullptr;
|
||||
intent_ids_obtained &=
|
||||
(android_system_intent_add_flags_ =
|
||||
jni_env->GetMethodID(android_system_intent_class_, "addFlags",
|
||||
"(I)Landroid/content/Intent;")) != nullptr;
|
||||
if (!intent_ids_obtained) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to get the "
|
||||
"intent class IDs");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
{
|
||||
jobject intent_action_view_local_ref = jni_env->GetStaticObjectField(
|
||||
android_system_intent_class_,
|
||||
android_system_intent_action_view_field_id_);
|
||||
if (!intent_action_view_local_ref) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to get the "
|
||||
"intent view action string");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
android_system_intent_action_view_ =
|
||||
jni_env->NewGlobalRef(intent_action_view_local_ref);
|
||||
jni_env->DeleteLocalRef(intent_action_view_local_ref);
|
||||
if (!android_system_intent_action_view_) {
|
||||
XELOGE(
|
||||
"InitializeAndroidSystemForApplicationContext: Failed to create a "
|
||||
"global reference to the intent view action string");
|
||||
ShutdownAndroidSystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
android_system_intent_flag_activity_new_task_ = jni_env->GetStaticIntField(
|
||||
android_system_intent_class_,
|
||||
android_system_intent_flag_activity_new_task_field_id_);
|
||||
}
|
||||
|
||||
android_system_initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShutdownAndroidSystem() {
|
||||
// May be called from InitializeAndroidSystemForApplicationContext as well.
|
||||
android_system_initialized_ = false;
|
||||
android_system_intent_add_flags_ = nullptr;
|
||||
android_system_intent_init_action_uri_ = nullptr;
|
||||
android_system_intent_flag_activity_new_task_field_id_ = nullptr;
|
||||
android_system_intent_action_view_field_id_ = nullptr;
|
||||
android_system_uri_parse_ = nullptr;
|
||||
android_system_application_context_start_activity_ = nullptr;
|
||||
JNIEnv* jni_env = GetAndroidThreadJNIEnv();
|
||||
if (jni_env) {
|
||||
if (android_system_intent_action_view_) {
|
||||
jni_env->DeleteGlobalRef(android_system_intent_action_view_);
|
||||
}
|
||||
if (android_system_intent_class_) {
|
||||
jni_env->DeleteGlobalRef(android_system_intent_class_);
|
||||
}
|
||||
if (android_system_uri_class_) {
|
||||
jni_env->DeleteGlobalRef(android_system_uri_class_);
|
||||
}
|
||||
if (android_system_application_context_class_) {
|
||||
jni_env->DeleteGlobalRef(android_system_application_context_class_);
|
||||
}
|
||||
}
|
||||
android_system_intent_action_view_ = nullptr;
|
||||
android_system_intent_class_ = nullptr;
|
||||
android_system_uri_class_ = nullptr;
|
||||
android_system_application_context_class_ = nullptr;
|
||||
}
|
||||
|
||||
void LaunchWebBrowser(const std::string_view url) {
|
||||
// TODO(Triang3l): Intent.ACTION_VIEW (need a Java VM for the thread -
|
||||
// possibly restrict this to the UI thread).
|
||||
assert_always();
|
||||
if (!android_system_initialized_) {
|
||||
return;
|
||||
}
|
||||
JNIEnv* jni_env = GetAndroidThreadJNIEnv();
|
||||
if (!jni_env) {
|
||||
return;
|
||||
}
|
||||
jobject application_context = GetAndroidApplicationContext();
|
||||
if (!application_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
jstring uri_string = jni_env->NewStringUTF(std::string(url).c_str());
|
||||
if (!uri_string) {
|
||||
XELOGE("LaunchWebBrowser: Failed to create the URI string");
|
||||
return;
|
||||
}
|
||||
jobject uri = jni_env->CallStaticObjectMethod(
|
||||
android_system_uri_class_, android_system_uri_parse_, uri_string);
|
||||
jni_env->DeleteLocalRef(uri_string);
|
||||
if (!uri) {
|
||||
XELOGE("LaunchWebBrowser: Failed to parse the URI");
|
||||
return;
|
||||
}
|
||||
jobject intent = jni_env->NewObject(android_system_intent_class_,
|
||||
android_system_intent_init_action_uri_,
|
||||
android_system_intent_action_view_, uri);
|
||||
jni_env->DeleteLocalRef(uri);
|
||||
if (!intent) {
|
||||
XELOGE("LaunchWebBrowser: Failed to create the intent");
|
||||
return;
|
||||
}
|
||||
// Start a new task - the user may want to be able to switch between the
|
||||
// emulator and the newly opened web browser, without having to quit the web
|
||||
// browser to return to the emulator. Also, since the application context, not
|
||||
// the activity, is used, the new task flag is required.
|
||||
{
|
||||
jobject intent_add_flags_result_local_ref = jni_env->CallObjectMethod(
|
||||
intent, android_system_intent_add_flags_,
|
||||
android_system_intent_flag_activity_new_task_);
|
||||
if (intent_add_flags_result_local_ref) {
|
||||
jni_env->DeleteLocalRef(intent_add_flags_result_local_ref);
|
||||
}
|
||||
}
|
||||
jni_env->CallVoidMethod(application_context,
|
||||
android_system_application_context_start_activity_,
|
||||
intent);
|
||||
jni_env->DeleteLocalRef(intent);
|
||||
}
|
||||
|
||||
void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); }
|
||||
|
|
|
@ -141,26 +141,14 @@ bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env,
|
|||
}
|
||||
AConfiguration_fromAssetManager(configuration_, asset_manager_);
|
||||
|
||||
// Initialize Xenia globals that may depend on the API level, as well as
|
||||
// logging.
|
||||
xe::InitializeAndroidAppFromMainThread(
|
||||
AConfiguration_getSdkVersion(configuration_));
|
||||
android_base_initialized_ = true;
|
||||
|
||||
// Initialize interfacing with the WindowedAppActivity.
|
||||
activity_ = ui_thread_jni_env_->NewGlobalRef(activity);
|
||||
if (!activity_) {
|
||||
XELOGE(
|
||||
"AndroidWindowedAppContext: Failed to create a global reference to the "
|
||||
"activity");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
// Get the activity class, needed for the application context here, and for
|
||||
// other activity interaction later.
|
||||
{
|
||||
jclass activity_class_local_ref =
|
||||
ui_thread_jni_env_->GetObjectClass(activity);
|
||||
if (!activity_class_local_ref) {
|
||||
XELOGE("AndroidWindowedAppContext: Failed to get the activity class");
|
||||
__android_log_write(ANDROID_LOG_ERROR, "AndroidWindowedAppContext",
|
||||
"Failed to get the activity class");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
@ -170,9 +158,46 @@ bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env,
|
|||
reinterpret_cast<jobject>(activity_class_local_ref));
|
||||
}
|
||||
if (!activity_class_) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "AndroidWindowedAppContext",
|
||||
"Failed to create a global reference to the activity class");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the application context.
|
||||
jmethodID activity_get_application_context = ui_thread_jni_env_->GetMethodID(
|
||||
activity_class_, "getApplicationContext", "()Landroid/content/Context;");
|
||||
if (!activity_get_application_context) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "AndroidWindowedAppContext",
|
||||
"Failed to get the getApplicationContext method of the activity");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
jobject application_context_init_ref = ui_thread_jni_env_->CallObjectMethod(
|
||||
activity, activity_get_application_context);
|
||||
if (!application_context_init_ref) {
|
||||
__android_log_write(
|
||||
ANDROID_LOG_ERROR, "AndroidWindowedAppContext",
|
||||
"Failed to get the application context from the activity");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize Xenia globals that may depend on the base globals and logging.
|
||||
xe::InitializeAndroidAppFromMainThread(
|
||||
AConfiguration_getSdkVersion(configuration_), ui_thread_jni_env_,
|
||||
application_context_init_ref);
|
||||
android_base_initialized_ = true;
|
||||
ui_thread_jni_env_->DeleteLocalRef(application_context_init_ref);
|
||||
|
||||
// Initialize interfacing with the WindowedAppActivity.
|
||||
activity_ = ui_thread_jni_env_->NewGlobalRef(activity);
|
||||
if (!activity_) {
|
||||
XELOGE(
|
||||
"AndroidWindowedAppContext: Failed to create a global reference to the "
|
||||
"activity class");
|
||||
"activity");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
@ -249,11 +274,6 @@ void AndroidWindowedAppContext::Shutdown() {
|
|||
}
|
||||
|
||||
activity_method_finish_ = nullptr;
|
||||
if (activity_class_) {
|
||||
ui_thread_jni_env_->DeleteGlobalRef(
|
||||
reinterpret_cast<jobject>(activity_class_));
|
||||
activity_class_ = nullptr;
|
||||
}
|
||||
if (activity_) {
|
||||
ui_thread_jni_env_->DeleteGlobalRef(activity_);
|
||||
activity_ = nullptr;
|
||||
|
@ -264,6 +284,12 @@ void AndroidWindowedAppContext::Shutdown() {
|
|||
android_base_initialized_ = false;
|
||||
}
|
||||
|
||||
if (activity_class_) {
|
||||
ui_thread_jni_env_->DeleteGlobalRef(
|
||||
reinterpret_cast<jobject>(activity_class_));
|
||||
activity_class_ = nullptr;
|
||||
}
|
||||
|
||||
if (configuration_) {
|
||||
AConfiguration_delete(configuration_);
|
||||
configuration_ = nullptr;
|
||||
|
|
|
@ -95,10 +95,11 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
|||
|
||||
AConfiguration* configuration_ = nullptr;
|
||||
|
||||
jclass activity_class_ = nullptr;
|
||||
|
||||
bool android_base_initialized_ = false;
|
||||
|
||||
jobject activity_ = nullptr;
|
||||
jclass activity_class_ = nullptr;
|
||||
jmethodID activity_method_finish_ = nullptr;
|
||||
|
||||
// May be read by non-UI threads in NotifyUILoopOfPendingFunctions.
|
||||
|
|
Loading…
Reference in New Issue