You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
support multiple Activities
This commit is contained in:
@@ -83,6 +83,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
|
|||||||
'src/api-impl-jni/android_app_NativeActivity.c',
|
'src/api-impl-jni/android_app_NativeActivity.c',
|
||||||
'src/api-impl-jni/android_opengl_GLES20.c',
|
'src/api-impl-jni/android_opengl_GLES20.c',
|
||||||
'src/api-impl-jni/location/android_location_LocationManager.c',
|
'src/api-impl-jni/location/android_location_LocationManager.c',
|
||||||
|
'src/api-impl-jni/app/android_app_Activity.c',
|
||||||
] + marshal_files,
|
] + marshal_files,
|
||||||
install: true,
|
install: true,
|
||||||
install_dir : get_option('libdir') / 'java/dex/android_translation_layer/natives',
|
install_dir : get_option('libdir') / 'java/dex/android_translation_layer/natives',
|
||||||
|
|||||||
99
src/api-impl-jni/app/android_app_Activity.c
Normal file
99
src/api-impl-jni/app/android_app_Activity.c
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "../defines.h"
|
||||||
|
#include "../util.h"
|
||||||
|
#include "android_app_Activity.h"
|
||||||
|
#include "../generated_headers/android_app_Activity.h"
|
||||||
|
|
||||||
|
static GList *activity_backlog = NULL;
|
||||||
|
static jobject activity_current = NULL;
|
||||||
|
|
||||||
|
static void activity_close(JNIEnv *env, jobject activity) {
|
||||||
|
// in case some exception was left unhandled in native code, print it here so we don't confuse it with an exception thrown by onDestroy
|
||||||
|
if((*env)->ExceptionCheck(env)) {
|
||||||
|
fprintf(stderr, "app_exit: seems there was a pending exception... :");
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- run the main activity's onDestroy -- */
|
||||||
|
(*env)->CallVoidMethod(env, activity, handle_cache.apk_main_activity.onDestroy, NULL);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void activity_update_current(JNIEnv *env) {
|
||||||
|
jobject activity_new = activity_backlog ? g_list_first(activity_backlog)->data : NULL;
|
||||||
|
|
||||||
|
if (activity_current != activity_new) {
|
||||||
|
if (activity_current) {
|
||||||
|
(*env)->CallVoidMethod(env, activity_current, handle_cache.apk_main_activity.onPause);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod(env, activity_current, handle_cache.apk_main_activity.onStop);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
}
|
||||||
|
if (activity_new) {
|
||||||
|
(*env)->CallVoidMethod(env, activity_new, handle_cache.apk_main_activity.onStart);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod(env, activity_new, handle_cache.apk_main_activity.onResume);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
}
|
||||||
|
activity_current = activity_new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void activity_window_ready(void) {
|
||||||
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
|
for (GList *l = activity_backlog; l != NULL; l = l->next) {
|
||||||
|
(*env)->CallVoidMethod(env, l->data, handle_cache.apk_main_activity.onWindowFocusChanged, true);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void activity_close_all(void) {
|
||||||
|
GList *activities, *l;
|
||||||
|
JNIEnv *env = get_jni_env();
|
||||||
|
// local backup of the backlog
|
||||||
|
activities = activity_backlog;
|
||||||
|
// deactivate all activities
|
||||||
|
activity_backlog = NULL;
|
||||||
|
activity_update_current(env);
|
||||||
|
// destroy all activities
|
||||||
|
for (l = activities; l != NULL; l = l->next) {
|
||||||
|
activity_close(env, l->data);
|
||||||
|
_UNREF(l->data);
|
||||||
|
}
|
||||||
|
g_list_free(activities);
|
||||||
|
}
|
||||||
|
|
||||||
|
void activity_start(JNIEnv *env, jobject activity_object) {
|
||||||
|
/* -- run the activity's onCreate -- */
|
||||||
|
(*env)->CallVoidMethod(env, activity_object, handle_cache.apk_main_activity.onCreate, NULL);
|
||||||
|
if((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
|
||||||
|
activity_backlog = g_list_prepend(activity_backlog, _REF(activity_object));
|
||||||
|
activity_update_current(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeFinish(JNIEnv *env, jobject this, jlong window) {
|
||||||
|
activity_backlog = g_list_remove(activity_backlog, this);
|
||||||
|
activity_update_current(env);
|
||||||
|
activity_close(env, this);
|
||||||
|
_UNREF(this);
|
||||||
|
if (activity_backlog == NULL)
|
||||||
|
gtk_window_close(GTK_WINDOW(_PTR(window)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeStartActivity(JNIEnv *env, jclass class, jobject activity) {
|
||||||
|
activity_start(env, activity);
|
||||||
|
}
|
||||||
5
src/api-impl-jni/app/android_app_Activity.h
Normal file
5
src/api-impl-jni/app/android_app_Activity.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
void activity_start(JNIEnv *env, jobject activity_object);
|
||||||
|
void activity_window_ready(void);
|
||||||
|
void activity_close_all(void);
|
||||||
31
src/api-impl-jni/generated_headers/android_app_Activity.h
Normal file
31
src/api-impl-jni/generated_headers/android_app_Activity.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class android_app_Activity */
|
||||||
|
|
||||||
|
#ifndef _Included_android_app_Activity
|
||||||
|
#define _Included_android_app_Activity
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#undef android_app_Activity_MODE_PRIVATE
|
||||||
|
#define android_app_Activity_MODE_PRIVATE 0L
|
||||||
|
/*
|
||||||
|
* Class: android_app_Activity
|
||||||
|
* Method: nativeFinish
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeFinish
|
||||||
|
(JNIEnv *, jobject, jlong);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: android_app_Activity
|
||||||
|
* Method: nativeStartActivity
|
||||||
|
* Signature: (Landroid/app/Activity;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeStartActivity
|
||||||
|
(JNIEnv *, jclass, jobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@@ -51,6 +51,8 @@ void set_up_handle_cache(JNIEnv *env)
|
|||||||
handle_cache.apk_main_activity.onWindowFocusChanged = _METHOD(handle_cache.apk_main_activity.class, "onWindowFocusChanged", "(Z)V");
|
handle_cache.apk_main_activity.onWindowFocusChanged = _METHOD(handle_cache.apk_main_activity.class, "onWindowFocusChanged", "(Z)V");
|
||||||
handle_cache.apk_main_activity.onResume = _METHOD(handle_cache.apk_main_activity.class, "onResume", "()V");
|
handle_cache.apk_main_activity.onResume = _METHOD(handle_cache.apk_main_activity.class, "onResume", "()V");
|
||||||
handle_cache.apk_main_activity.onDestroy = _METHOD(handle_cache.apk_main_activity.class, "onDestroy", "()V");
|
handle_cache.apk_main_activity.onDestroy = _METHOD(handle_cache.apk_main_activity.class, "onDestroy", "()V");
|
||||||
|
handle_cache.apk_main_activity.onStop = _METHOD(handle_cache.apk_main_activity.class, "onStop", "()V");
|
||||||
|
handle_cache.apk_main_activity.onPause = _METHOD(handle_cache.apk_main_activity.class, "onPause", "()V");
|
||||||
|
|
||||||
handle_cache.attribute_set.class = _REF((*env)->FindClass(env, "android/util/AttributeSet"));
|
handle_cache.attribute_set.class = _REF((*env)->FindClass(env, "android/util/AttributeSet"));
|
||||||
if((*env)->ExceptionCheck(env))
|
if((*env)->ExceptionCheck(env))
|
||||||
|
|||||||
@@ -8,12 +8,13 @@
|
|||||||
struct handle_cache {
|
struct handle_cache {
|
||||||
struct {
|
struct {
|
||||||
jclass class;
|
jclass class;
|
||||||
jobject object;
|
|
||||||
jmethodID onCreate;
|
jmethodID onCreate;
|
||||||
jmethodID onStart;
|
jmethodID onStart;
|
||||||
jmethodID onResume;
|
jmethodID onResume;
|
||||||
jmethodID onWindowFocusChanged;
|
jmethodID onWindowFocusChanged;
|
||||||
jmethodID onDestroy;
|
jmethodID onDestroy;
|
||||||
|
jmethodID onStop;
|
||||||
|
jmethodID onPause;
|
||||||
} apk_main_activity;
|
} apk_main_activity;
|
||||||
struct {
|
struct {
|
||||||
jclass class;
|
jclass class;
|
||||||
@@ -78,6 +79,8 @@ struct handle_cache {
|
|||||||
|
|
||||||
extern struct handle_cache handle_cache;
|
extern struct handle_cache handle_cache;
|
||||||
|
|
||||||
|
JNIEnv * get_jni_env(void);
|
||||||
|
|
||||||
const char * attribute_set_get_string(JNIEnv *env, jobject attrs, char *attribute, char *schema);
|
const char * attribute_set_get_string(JNIEnv *env, jobject attrs, char *attribute, char *schema);
|
||||||
int attribute_set_get_int(JNIEnv *env, jobject attrs, char *attribute, char *schema, int default_value);
|
int attribute_set_get_int(JNIEnv *env, jobject attrs, char *attribute, char *schema, int default_value);
|
||||||
void set_up_handle_cache(JNIEnv *env);
|
void set_up_handle_cache(JNIEnv *env);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class Activity extends Context {
|
|||||||
LayoutInflater layout_inflater;
|
LayoutInflater layout_inflater;
|
||||||
Window window = new Window();
|
Window window = new Window();
|
||||||
int requested_orientation = -1 /*ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED*/; // dummy
|
int requested_orientation = -1 /*ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED*/; // dummy
|
||||||
|
private Intent intent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to be called from native code to construct main activity
|
* Helper function to be called from native code to construct main activity
|
||||||
@@ -56,6 +57,7 @@ public class Activity extends Context {
|
|||||||
|
|
||||||
public Activity() {
|
public Activity() {
|
||||||
layout_inflater = new LayoutInflater();
|
layout_inflater = new LayoutInflater();
|
||||||
|
intent = new Intent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public View root_view;
|
public View root_view;
|
||||||
@@ -77,8 +79,7 @@ public class Activity extends Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Intent getIntent() {
|
public Intent getIntent() {
|
||||||
// return null; // this is the main activity, and it wasn't opened as a result of someone calling "open with"
|
return intent;
|
||||||
return new Intent(); // seems some apps don't consider this nullable...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRequestedOrientation() {
|
public int getRequestedOrientation() {
|
||||||
@@ -223,10 +224,45 @@ public class Activity extends Context {
|
|||||||
|
|
||||||
public void startActivityForResult(Intent intent, int requestCode) {
|
public void startActivityForResult(Intent intent, int requestCode) {
|
||||||
System.out.println("startActivityForResult(" + intent + ", " + requestCode + ") called, but we don't currently support multiple activities");
|
System.out.println("startActivityForResult(" + intent + ", " + requestCode + ") called, but we don't currently support multiple activities");
|
||||||
|
if (intent.getComponent() != null) {
|
||||||
|
try {
|
||||||
|
Class<? extends Activity> cls = Class.forName(intent.getComponent().getClassName()).asSubclass(Activity.class);
|
||||||
|
Constructor<? extends Activity> constructor = cls.getConstructor();
|
||||||
|
Activity activity = constructor.newInstance();
|
||||||
|
activity.intent = intent;
|
||||||
|
activity.window = getWindow();
|
||||||
|
nativeStartActivity(activity);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); // RESULT_CANCELED is the only pre-defined return value, so hopefully it works out for us
|
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); // RESULT_CANCELED is the only pre-defined return value, so hopefully it works out for us
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); // RESULT_CANCELED is the only pre-defined return value, so hopefully it works out for us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startActivity(Intent intent) {
|
||||||
|
System.out.println("startActivity(" + intent + ") called");
|
||||||
|
try {
|
||||||
|
Class<? extends Activity> cls = Class.forName(intent.getComponent().getClassName()).asSubclass(Activity.class);
|
||||||
|
Constructor<? extends Activity> constructor = cls.getConstructor();
|
||||||
|
Activity activity = constructor.newInstance();
|
||||||
|
activity.intent = intent;
|
||||||
|
activity.window = getWindow();
|
||||||
|
nativeStartActivity(activity);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final void showDialog(int id) {
|
public final void showDialog(int id) {
|
||||||
System.out.println("Activity.showDialog(" + id + ") called");
|
System.out.println("Activity.showDialog(" + id + ") called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void finish() {
|
||||||
|
nativeFinish(getWindow().native_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeFinish(long native_window);
|
||||||
|
private static native void nativeStartActivity(Activity activity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "../api-impl-jni/defines.h"
|
#include "../api-impl-jni/defines.h"
|
||||||
#include "../api-impl-jni/util.h"
|
#include "../api-impl-jni/util.h"
|
||||||
|
#include "../api-impl-jni/app/android_app_Activity.h"
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -34,17 +35,7 @@ GtkWidget *window;
|
|||||||
|
|
||||||
gboolean app_exit(GtkWindow* self, JNIEnv *env) // TODO: do more cleanup?
|
gboolean app_exit(GtkWindow* self, JNIEnv *env) // TODO: do more cleanup?
|
||||||
{
|
{
|
||||||
// in case some exception was left unhandled in native code, print it here so we don't confuse it with an exception thrown by onDestroy
|
activity_close_all();
|
||||||
if((*env)->ExceptionCheck(env)) {
|
|
||||||
fprintf(stderr, "app_exit: seems there was a pending exception... :");
|
|
||||||
(*env)->ExceptionDescribe(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- run the main activity's onDestroy -- */
|
|
||||||
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onDestroy, NULL);
|
|
||||||
if((*env)->ExceptionCheck(env))
|
|
||||||
(*env)->ExceptionDescribe(env);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,10 +119,7 @@ JNIEnv* create_vm(char *api_impl_jar, char *apk_classpath, char *microg_apk, cha
|
|||||||
gboolean hacky_on_window_focus_changed_callback(JNIEnv *env)
|
gboolean hacky_on_window_focus_changed_callback(JNIEnv *env)
|
||||||
{
|
{
|
||||||
if(gtk_widget_get_width(window) != 0) {
|
if(gtk_widget_get_width(window) != 0) {
|
||||||
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onWindowFocusChanged, true);
|
activity_window_ready();
|
||||||
if((*env)->ExceptionCheck(env))
|
|
||||||
(*env)->ExceptionDescribe(env);
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +158,7 @@ static void open(GtkApplication *app, GFile** files, gint nfiles, const gchar* h
|
|||||||
int errno_libdir;
|
int errno_libdir;
|
||||||
int errno_localdir;
|
int errno_localdir;
|
||||||
int ret;
|
int ret;
|
||||||
|
jobject activity_object;
|
||||||
|
|
||||||
char *apk_classpath = g_file_get_path(files[0]);
|
char *apk_classpath = g_file_get_path(files[0]);
|
||||||
char *apk_name = g_file_get_basename(files[0]);
|
char *apk_name = g_file_get_basename(files[0]);
|
||||||
@@ -364,26 +353,13 @@ static void open(GtkApplication *app, GFile** files, gint nfiles, const gchar* h
|
|||||||
prepare_main_looper(env);
|
prepare_main_looper(env);
|
||||||
|
|
||||||
// construct main Activity
|
// construct main Activity
|
||||||
handle_cache.apk_main_activity.object = _REF((*env)->CallStaticObjectMethod(env, handle_cache.apk_main_activity.class,
|
activity_object = (*env)->CallStaticObjectMethod(env, handle_cache.apk_main_activity.class,
|
||||||
_STATIC_METHOD(handle_cache.apk_main_activity.class, "createMainActivity", "(Ljava/lang/String;J)Landroid/app/Activity;"),
|
_STATIC_METHOD(handle_cache.apk_main_activity.class, "createMainActivity", "(Ljava/lang/String;J)Landroid/app/Activity;"),
|
||||||
_JSTRING(d->apk_main_activity_class), _INTPTR(window)));
|
_JSTRING(d->apk_main_activity_class), _INTPTR(window));
|
||||||
if((*env)->ExceptionCheck(env))
|
if((*env)->ExceptionCheck(env))
|
||||||
(*env)->ExceptionDescribe(env);
|
(*env)->ExceptionDescribe(env);
|
||||||
|
|
||||||
/* -- run the main activity's onCreate -- */
|
activity_start(env, activity_object);
|
||||||
|
|
||||||
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onCreate, NULL);
|
|
||||||
if((*env)->ExceptionCheck(env))
|
|
||||||
(*env)->ExceptionDescribe(env);
|
|
||||||
|
|
||||||
// TODO: some apps wait for this to actually do stuff
|
|
||||||
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onStart);
|
|
||||||
if((*env)->ExceptionCheck(env))
|
|
||||||
(*env)->ExceptionDescribe(env);
|
|
||||||
|
|
||||||
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onResume);
|
|
||||||
if((*env)->ExceptionCheck(env))
|
|
||||||
(*env)->ExceptionDescribe(env);
|
|
||||||
|
|
||||||
g_timeout_add(10, G_SOURCE_FUNC(hacky_on_window_focus_changed_callback), env);
|
g_timeout_add(10, G_SOURCE_FUNC(hacky_on_window_focus_changed_callback), env);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user