diff options
Diffstat (limited to 'platform/android')
27 files changed, 213 insertions, 2177 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub index da2219b9e4..6d5af99bc5 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -10,9 +10,7 @@ from detect import get_ndk_version android_files = [ 'os_android.cpp', - 'godot_android.cpp', 'file_access_android.cpp', - 'dir_access_android.cpp', 'audio_driver_opensl.cpp', 'file_access_jandroid.cpp', 'dir_access_jandroid.cpp', @@ -25,7 +23,6 @@ android_files = [ thirdparty_files = [ 'ifaddrs_android.cpp', - 'android_native_app_glue.c', 'cpu-features.c', ] diff --git a/platform/android/android_native_app_glue.c b/platform/android/android_native_app_glue.c deleted file mode 100644 index 965f6284cd..0000000000 --- a/platform/android/android_native_app_glue.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifdef ANDROID_NATIVE_ACTIVITY - -#include <jni.h> - - -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <sys/resource.h> - -#include "android_native_app_glue.h" -#include <android/log.h> - -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) - -static void free_saved_state(struct android_app* android_app) { - pthread_mutex_lock(&android_app->mutex); - if (android_app->savedState != NULL) { - free(android_app->savedState); - android_app->savedState = NULL; - android_app->savedStateSize = 0; - } - pthread_mutex_unlock(&android_app->mutex); -} - -int8_t android_app_read_cmd(struct android_app* android_app) { - int8_t cmd; - if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { - switch (cmd) { - case APP_CMD_SAVE_STATE: - free_saved_state(android_app); - break; - } - return cmd; - } else { - LOGI("No data on command pipe!"); - } - return -1; -} - -static void print_cur_config(struct android_app* android_app) { - char lang[2], country[2]; - AConfiguration_getLanguage(android_app->config, lang); - AConfiguration_getCountry(android_app->config, country); - - LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " - "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " - "modetype=%d modenight=%d", - AConfiguration_getMcc(android_app->config), - AConfiguration_getMnc(android_app->config), - lang[0], lang[1], country[0], country[1], - AConfiguration_getOrientation(android_app->config), - AConfiguration_getTouchscreen(android_app->config), - AConfiguration_getDensity(android_app->config), - AConfiguration_getKeyboard(android_app->config), - AConfiguration_getNavigation(android_app->config), - AConfiguration_getKeysHidden(android_app->config), - AConfiguration_getNavHidden(android_app->config), - AConfiguration_getSdkVersion(android_app->config), - AConfiguration_getScreenSize(android_app->config), - AConfiguration_getScreenLong(android_app->config), - AConfiguration_getUiModeType(android_app->config), - AConfiguration_getUiModeNight(android_app->config)); -} - -void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { - switch (cmd) { - case APP_CMD_INPUT_CHANGED: - LOGI("APP_CMD_INPUT_CHANGED\n"); - pthread_mutex_lock(&android_app->mutex); - if (android_app->inputQueue != NULL) { - AInputQueue_detachLooper(android_app->inputQueue); - } - android_app->inputQueue = android_app->pendingInputQueue; - if (android_app->inputQueue != NULL) { - LOGI("Attaching input queue to looper"); - AInputQueue_attachLooper(android_app->inputQueue, - android_app->looper, LOOPER_ID_INPUT, NULL, - &android_app->inputPollSource); - } - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_INIT_WINDOW: - LOGI("APP_CMD_INIT_WINDOW\n"); - pthread_mutex_lock(&android_app->mutex); - android_app->window = android_app->pendingWindow; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_TERM_WINDOW: - LOGI("APP_CMD_TERM_WINDOW\n"); - pthread_cond_broadcast(&android_app->cond); - break; - - case APP_CMD_RESUME: - case APP_CMD_START: - case APP_CMD_PAUSE: - case APP_CMD_STOP: - LOGI("activityState=%d\n", cmd); - pthread_mutex_lock(&android_app->mutex); - android_app->activityState = cmd; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_CONFIG_CHANGED: - LOGI("APP_CMD_CONFIG_CHANGED\n"); - AConfiguration_fromAssetManager(android_app->config, - android_app->activity->assetManager); - print_cur_config(android_app); - break; - - case APP_CMD_DESTROY: - LOGI("APP_CMD_DESTROY\n"); - android_app->destroyRequested = 1; - break; - } -} - -void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { - switch (cmd) { - case APP_CMD_TERM_WINDOW: - LOGI("APP_CMD_TERM_WINDOW\n"); - pthread_mutex_lock(&android_app->mutex); - android_app->window = NULL; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_SAVE_STATE: - LOGI("APP_CMD_SAVE_STATE\n"); - pthread_mutex_lock(&android_app->mutex); - android_app->stateSaved = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_RESUME: - free_saved_state(android_app); - break; - } -} - -void app_dummy() { - -} - -static void android_app_destroy(struct android_app* android_app) { - LOGI("android_app_destroy!"); - free_saved_state(android_app); - pthread_mutex_lock(&android_app->mutex); - if (android_app->inputQueue != NULL) { - AInputQueue_detachLooper(android_app->inputQueue); - } - AConfiguration_delete(android_app->config); - android_app->destroyed = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - // Can't touch android_app object after this. -} - -static void process_input(struct android_app* app, struct android_poll_source* source) { - AInputEvent* event = NULL; - if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { - LOGI("New input event: type=%d\n", AInputEvent_getType(event)); - if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { - return; - } - int32_t handled = 0; - if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); - AInputQueue_finishEvent(app->inputQueue, event, handled); - } else { - LOGI("Failure reading next input event: %s\n", strerror(errno)); - } -} - -static void process_cmd(struct android_app* app, struct android_poll_source* source) { - int8_t cmd = android_app_read_cmd(app); - android_app_pre_exec_cmd(app, cmd); - if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); - android_app_post_exec_cmd(app, cmd); -} - -static void* android_app_entry(void* param) { - struct android_app* android_app = (struct android_app*)param; - - android_app->config = AConfiguration_new(); - AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); - - print_cur_config(android_app); - - android_app->cmdPollSource.id = LOOPER_ID_MAIN; - android_app->cmdPollSource.app = android_app; - android_app->cmdPollSource.process = process_cmd; - android_app->inputPollSource.id = LOOPER_ID_INPUT; - android_app->inputPollSource.app = android_app; - android_app->inputPollSource.process = process_input; - - ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); - ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, - &android_app->cmdPollSource); - android_app->looper = looper; - - pthread_mutex_lock(&android_app->mutex); - android_app->running = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - - android_main(android_app); - - android_app_destroy(android_app); - return NULL; -} - -// -------------------------------------------------------------------- -// Native activity interaction (called from main thread) -// -------------------------------------------------------------------- - -static struct android_app* android_app_create(ANativeActivity* activity, - void* savedState, size_t savedStateSize) { - struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); - memset(android_app, 0, sizeof(struct android_app)); - android_app->activity = activity; - - pthread_mutex_init(&android_app->mutex, NULL); - pthread_cond_init(&android_app->cond, NULL); - - if (savedState != NULL) { - android_app->savedState = malloc(savedStateSize); - android_app->savedStateSize = savedStateSize; - memcpy(android_app->savedState, savedState, savedStateSize); - } - - int msgpipe[2]; - if (pipe(msgpipe)) { - LOGI("could not create pipe: %s", strerror(errno)); - } - android_app->msgread = msgpipe[0]; - android_app->msgwrite = msgpipe[1]; - - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&android_app->thread, &attr, android_app_entry, android_app); - - // Wait for thread to start. - pthread_mutex_lock(&android_app->mutex); - while (!android_app->running) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); - - return android_app; -} - -static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { - if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { - LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); - } -} - -static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { - pthread_mutex_lock(&android_app->mutex); - android_app->pendingInputQueue = inputQueue; - android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); - while (android_app->inputQueue != android_app->pendingInputQueue) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { - pthread_mutex_lock(&android_app->mutex); - if (android_app->pendingWindow != NULL) { - android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); - } - android_app->pendingWindow = window; - if (window != NULL) { - android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); - } - while (android_app->window != android_app->pendingWindow) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { - pthread_mutex_lock(&android_app->mutex); - android_app_write_cmd(android_app, cmd); - while (android_app->activityState != cmd) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_free(struct android_app* android_app) { - pthread_mutex_lock(&android_app->mutex); - android_app_write_cmd(android_app, APP_CMD_DESTROY); - while (!android_app->destroyed) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); - - close(android_app->msgread); - close(android_app->msgwrite); - pthread_cond_destroy(&android_app->cond); - pthread_mutex_destroy(&android_app->mutex); - free(android_app); -} - -static void onDestroy(ANativeActivity* activity) { - LOGI("Destroy: %p\n", activity); - android_app_free((struct android_app*)activity->instance); -} - -static void onStart(ANativeActivity* activity) { - LOGI("Start: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); -} - -static void onResume(ANativeActivity* activity) { - LOGI("Resume: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); -} - -static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { - struct android_app* android_app = (struct android_app*)activity->instance; - void* savedState = NULL; - - LOGI("SaveInstanceState: %p\n", activity); - pthread_mutex_lock(&android_app->mutex); - android_app->stateSaved = 0; - android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); - while (!android_app->stateSaved) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - - if (android_app->savedState != NULL) { - savedState = android_app->savedState; - *outLen = android_app->savedStateSize; - android_app->savedState = NULL; - android_app->savedStateSize = 0; - } - - pthread_mutex_unlock(&android_app->mutex); - - return savedState; -} - -static void onPause(ANativeActivity* activity) { - LOGI("Pause: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); -} - -static void onStop(ANativeActivity* activity) { - LOGI("Stop: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); -} - -static void onConfigurationChanged(ANativeActivity* activity) { - struct android_app* android_app = (struct android_app*)activity->instance; - LOGI("ConfigurationChanged: %p\n", activity); - android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); -} - -static void onLowMemory(ANativeActivity* activity) { - struct android_app* android_app = (struct android_app*)activity->instance; - LOGI("LowMemory: %p\n", activity); - android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); -} - -static void onWindowFocusChanged(ANativeActivity* activity, int focused) { - LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); - android_app_write_cmd((struct android_app*)activity->instance, - focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); -} - -static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { - LOGI("NativeWindowCreated: %p -- %p\n", activity, window); - android_app_set_window((struct android_app*)activity->instance, window); -} - -static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { - LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); - android_app_set_window((struct android_app*)activity->instance, NULL); -} - -static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { - LOGI("InputQueueCreated: %p -- %p\n", activity, queue); - android_app_set_input((struct android_app*)activity->instance, queue); -} - -static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { - LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); - android_app_set_input((struct android_app*)activity->instance, NULL); -} - -void ANativeActivity_onCreate(ANativeActivity* activity, - void* savedState, size_t savedStateSize) { - LOGI("Creating: %p\n", activity); - activity->callbacks->onDestroy = onDestroy; - activity->callbacks->onStart = onStart; - activity->callbacks->onResume = onResume; - activity->callbacks->onSaveInstanceState = onSaveInstanceState; - activity->callbacks->onPause = onPause; - activity->callbacks->onStop = onStop; - activity->callbacks->onConfigurationChanged = onConfigurationChanged; - activity->callbacks->onLowMemory = onLowMemory; - activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; - activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; - activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; - activity->callbacks->onInputQueueCreated = onInputQueueCreated; - activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; - - activity->instance = android_app_create(activity, savedState, savedStateSize); -} -#endif diff --git a/platform/android/android_native_app_glue.h b/platform/android/android_native_app_glue.h deleted file mode 100644 index 36278d4c66..0000000000 --- a/platform/android/android_native_app_glue.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef _ANDROID_NATIVE_APP_GLUE_H -#define _ANDROID_NATIVE_APP_GLUE_H -#ifdef ANDROID_NATIVE_ACTIVITY - -#include <poll.h> -#include <pthread.h> -#include <sched.h> - -#include <android/configuration.h> -#include <android/looper.h> -#include <android/native_activity.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * The native activity interface provided by <android/native_activity.h> - * is based on a set of application-provided callbacks that will be called - * by the Activity's main thread when certain events occur. - * - * This means that each one of this callbacks _should_ _not_ block, or they - * risk having the system force-close the application. This programming - * model is direct, lightweight, but constraining. - * - * The 'threaded_native_app' static library is used to provide a different - * execution model where the application can implement its own main event - * loop in a different thread instead. Here's how it works: - * - * 1/ The application must provide a function named "android_main()" that - * will be called when the activity is created, in a new thread that is - * distinct from the activity's main thread. - * - * 2/ android_main() receives a pointer to a valid "android_app" structure - * that contains references to other important objects, e.g. the - * ANativeActivity obejct instance the application is running in. - * - * 3/ the "android_app" object holds an ALooper instance that already - * listens to two important things: - * - * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX - * declarations below. - * - * - input events coming from the AInputQueue attached to the activity. - * - * Each of these correspond to an ALooper identifier returned by - * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, - * respectively. - * - * Your application can use the same ALooper to listen to additional - * file-descriptors. They can either be callback based, or with return - * identifiers starting with LOOPER_ID_USER. - * - * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, - * the returned data will point to an android_poll_source structure. You - * can call the process() function on it, and fill in android_app->onAppCmd - * and android_app->onInputEvent to be called for your own processing - * of the event. - * - * Alternatively, you can call the low-level functions to read and process - * the data directly... look at the process_cmd() and process_input() - * implementations in the glue to see how to do this. - * - * See the sample named "native-activity" that comes with the NDK with a - * full usage example. Also look at the JavaDoc of NativeActivity. - */ - -struct android_app; - -/** - * Data associated with an ALooper fd that will be returned as the "outData" - * when that source has data ready. - */ -struct android_poll_source { - // The identifier of this source. May be LOOPER_ID_MAIN or - // LOOPER_ID_INPUT. - int32_t id; - - // The android_app this ident is associated with. - struct android_app* app; - - // Function to call to perform the standard processing of data from - // this source. - void (*process)(struct android_app* app, struct android_poll_source* source); -}; - -/** - * This is the interface for the standard glue code of a threaded - * application. In this model, the application's code is running - * in its own thread separate from the main thread of the process. - * It is not required that this thread be associated with the Java - * VM, although it will need to be in order to make JNI calls any - * Java objects. - */ -struct android_app { - // The application can place a pointer to its own state object - // here if it likes. - void* userData; - - // Fill this in with the function to process main app commands (APP_CMD_*) - void (*onAppCmd)(struct android_app* app, int32_t cmd); - - // Fill this in with the function to process input events. At this point - // the event has already been pre-dispatched, and it will be finished upon - // return. Return 1 if you have handled the event, 0 for any default - // dispatching. - int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); - - // The ANativeActivity object instance that this app is running in. - ANativeActivity* activity; - - // The current configuration the app is running in. - AConfiguration* config; - - // This is the last instance's saved state, as provided at creation time. - // It is NULL if there was no state. You can use this as you need; the - // memory will remain around until you call android_app_exec_cmd() for - // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. - // These variables should only be changed when processing a APP_CMD_SAVE_STATE, - // at which point they will be initialized to NULL and you can malloc your - // state and place the information here. In that case the memory will be - // freed for you later. - void* savedState; - size_t savedStateSize; - - // The ALooper associated with the app's thread. - ALooper* looper; - - // When non-NULL, this is the input queue from which the app will - // receive user input events. - AInputQueue* inputQueue; - - // When non-NULL, this is the window surface that the app can draw in. - ANativeWindow* window; - - // Current content rectangle of the window; this is the area where the - // window's content should be placed to be seen by the user. - ARect contentRect; - - // Current state of the app's activity. May be either APP_CMD_START, - // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. - int activityState; - - // This is non-zero when the application's NativeActivity is being - // destroyed and waiting for the app thread to complete. - int destroyRequested; - - // ------------------------------------------------- - // Below are "private" implementation of the glue code. - - pthread_mutex_t mutex; - pthread_cond_t cond; - - int msgread; - int msgwrite; - - pthread_t thread; - - struct android_poll_source cmdPollSource; - struct android_poll_source inputPollSource; - - int running; - int stateSaved; - int destroyed; - int redrawNeeded; - AInputQueue* pendingInputQueue; - ANativeWindow* pendingWindow; - ARect pendingContentRect; -}; - -enum { - /** - * Looper data ID of commands coming from the app's main thread, which - * is returned as an identifier from ALooper_pollOnce(). The data for this - * identifier is a pointer to an android_poll_source structure. - * These can be retrieved and processed with android_app_read_cmd() - * and android_app_exec_cmd(). - */ - LOOPER_ID_MAIN = 1, - - /** - * Looper data ID of events coming from the AInputQueue of the - * application's window, which is returned as an identifier from - * ALooper_pollOnce(). The data for this identifier is a pointer to an - * android_poll_source structure. These can be read via the inputQueue - * object of android_app. - */ - LOOPER_ID_INPUT = 2, - - /** - * Start of user-defined ALooper identifiers. - */ - LOOPER_ID_USER = 3, -}; - -enum { - /** - * Command from main thread: the AInputQueue has changed. Upon processing - * this command, android_app->inputQueue will be updated to the new queue - * (or NULL). - */ - APP_CMD_INPUT_CHANGED, - - /** - * Command from main thread: a new ANativeWindow is ready for use. Upon - * receiving this command, android_app->window will contain the new window - * surface. - */ - APP_CMD_INIT_WINDOW, - - /** - * Command from main thread: the existing ANativeWindow needs to be - * terminated. Upon receiving this command, android_app->window still - * contains the existing window; after calling android_app_exec_cmd - * it will be set to NULL. - */ - APP_CMD_TERM_WINDOW, - - /** - * Command from main thread: the current ANativeWindow has been resized. - * Please redraw with its new size. - */ - APP_CMD_WINDOW_RESIZED, - - /** - * Command from main thread: the system needs that the current ANativeWindow - * be redrawn. You should redraw the window before handing this to - * android_app_exec_cmd() in order to avoid transient drawing glitches. - */ - APP_CMD_WINDOW_REDRAW_NEEDED, - - /** - * Command from main thread: the content area of the window has changed, - * such as from the soft input window being shown or hidden. You can - * find the new content rect in android_app::contentRect. - */ - APP_CMD_CONTENT_RECT_CHANGED, - - /** - * Command from main thread: the app's activity window has gained - * input focus. - */ - APP_CMD_GAINED_FOCUS, - - /** - * Command from main thread: the app's activity window has lost - * input focus. - */ - APP_CMD_LOST_FOCUS, - - /** - * Command from main thread: the current device configuration has changed. - */ - APP_CMD_CONFIG_CHANGED, - - /** - * Command from main thread: the system is running low on memory. - * Try to reduce your memory use. - */ - APP_CMD_LOW_MEMORY, - - /** - * Command from main thread: the app's activity has been started. - */ - APP_CMD_START, - - /** - * Command from main thread: the app's activity has been resumed. - */ - APP_CMD_RESUME, - - /** - * Command from main thread: the app should generate a new saved state - * for itself, to restore from later if needed. If you have saved state, - * allocate it with malloc and place it in android_app.savedState with - * the size in android_app.savedStateSize. The will be freed for you - * later. - */ - APP_CMD_SAVE_STATE, - - /** - * Command from main thread: the app's activity has been paused. - */ - APP_CMD_PAUSE, - - /** - * Command from main thread: the app's activity has been stopped. - */ - APP_CMD_STOP, - - /** - * Command from main thread: the app's activity is being destroyed, - * and waiting for the app thread to clean up and exit before proceeding. - */ - APP_CMD_DESTROY, -}; - -/** - * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next - * app command message. - */ -int8_t android_app_read_cmd(struct android_app* android_app); - -/** - * Call with the command returned by android_app_read_cmd() to do the - * initial pre-processing of the given command. You can perform your own - * actions for the command after calling this function. - */ -void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); - -/** - * Call with the command returned by android_app_read_cmd() to do the - * final post-processing of the given command. You must have done your own - * actions for the command before calling this function. - */ -void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); - -/** - * Dummy function you can call to ensure glue code isn't stripped. - */ -void app_dummy(); - -/** - * This is the function that application code must implement, representing - * the main entry to the app. - */ -extern void android_main(struct android_app* app); - -#ifdef __cplusplus -} -#endif - -#endif /* _ANDROID_NATIVE_APP_GLUE_H */ -#endif diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 4fab40d534..b75a4a3869 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -34,8 +34,6 @@ #include "core/project_settings.h" #include "thread_jandroid.h" -#ifndef ANDROID_NATIVE_ACTIVITY - AudioDriverAndroid *AudioDriverAndroid::s_ad = NULL; jobject AudioDriverAndroid::io; @@ -204,5 +202,3 @@ AudioDriverAndroid::AudioDriverAndroid() { s_ad = this; active = false; } - -#endif diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h index 763f0e9b5a..3c51ed746d 100644 --- a/platform/android/audio_driver_jandroid.h +++ b/platform/android/audio_driver_jandroid.h @@ -33,8 +33,6 @@ #include "servers/audio_server.h" -#ifndef ANDROID_NATIVE_ACTIVITY - #include "java_glue.h" class AudioDriverAndroid : public AudioDriver { @@ -78,5 +76,4 @@ public: AudioDriverAndroid(); }; -#endif #endif // AUDIO_DRIVER_ANDROID_H diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index 1603ea70d9..18ffc74fc3 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -14,9 +14,9 @@ apply plugin: 'com.android.application' allprojects { repositories { - jcenter() mavenCentral() google() + jcenter() $$GRADLE_REPOSITORY_URLS$$ } } diff --git a/platform/android/detect.py b/platform/android/detect.py index 953a2fa6d2..7a728e4ef1 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -186,7 +186,7 @@ def configure(env): env.PrependENVPath('PATH', tools_path) ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = compiler_path + '/clang' env['CXX'] = compiler_path + '/clang++' else: @@ -223,7 +223,7 @@ def configure(env): else: print("Using NDK deprecated headers") env.Append(CPPFLAGS=["-isystem", lib_sysroot + "/usr/include"]) - + env.Append(CPPFLAGS='-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing'.split()) env.Append(CPPFLAGS='-DNO_STATVFS -DGLES_ENABLED'.split()) @@ -267,12 +267,12 @@ def configure(env): env.Append(LINKFLAGS=['-shared', '--sysroot=' + lib_sysroot, '-Wl,--warn-shared-textrel']) if mt_link: env.Append(LINKFLAGS=['-Wl,--threads']) - + if env["android_arch"] == "armv7": env.Append(LINKFLAGS='-Wl,--fix-cortex-a8'.split()) env.Append(LINKFLAGS='-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now'.split()) env.Append(LINKFLAGS='-Wl,-soname,libgodot_android.so -Wl,--gc-sections'.split()) - + env.Append(LINKFLAGS=target_opts) env.Append(LINKFLAGS=common_opts) @@ -293,7 +293,7 @@ def configure(env): # Return NDK version string in source.properties (adapted from the Chromium project). def get_ndk_version(path): - if path == None: + if path is None: return None prop_file_path = os.path.join(path, "source.properties") try: diff --git a/platform/android/dir_access_android.cpp b/platform/android/dir_access_android.cpp deleted file mode 100644 index 402da4527e..0000000000 --- a/platform/android/dir_access_android.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/*************************************************************************/ -/* dir_access_android.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifdef ANDROID_NATIVE_ACTIVITY -#include "dir_access_android.h" -#include "file_access_android.h" - -DirAccess *DirAccessAndroid::create_fs() { - - return memnew(DirAccessAndroid); -} - -Error DirAccessAndroid::list_dir_begin() { - - list_dir_end(); - - AAssetDir *aad = AAssetManager_openDir(FileAccessAndroid::asset_manager, current_dir.utf8().get_data()); - if (!aad) - return ERR_CANT_OPEN; //nothing - - return OK; -} - -String DirAccessAndroid::get_next() { - - const char *fn = AAssetDir_getNextFileName(aad); - if (!fn) - return ""; - String s; - s.parse_utf8(fn); - current = s; - return s; -} - -bool DirAccessAndroid::current_is_dir() const { - - String sd; - if (current_dir == "") - sd = current; - else - sd = current_dir + "/" + current; - - AAssetDir *aad2 = AAssetManager_openDir(FileAccessAndroid::asset_manager, sd.utf8().get_data()); - if (aad2) { - - AAssetDir_close(aad2); - return true; - } - - return false; -} - -bool DirAccessAndroid::current_is_hidden() const { - return current != "." && current != ".." && current.begins_with("."); -} - -void DirAccessAndroid::list_dir_end() { - - if (aad == NULL) - return; - - AAssetDir_close(aad); - aad = NULL; -} - -int DirAccessAndroid::get_drive_count() { - - return 0; -} - -String DirAccessAndroid::get_drive(int p_drive) { - - return ""; -} - -Error DirAccessAndroid::change_dir(String p_dir) { - - p_dir = p_dir.simplify_path(); - - if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == "")) - return OK; - - String new_dir; - - if (p_dir.begins_with("/")) - new_dir = p_dir.substr(1, p_dir.length()); - else if (p_dir.begins_with("res://")) - new_dir = p_dir.substr(6, p_dir.length()); - else //relative - new_dir = new_dir + "/" + p_dir; - - //test if newdir exists - new_dir = new_dir.simplify_path(); - - AAssetDir *aad = AAssetManager_openDir(FileAccessAndroid::asset_manager, new_dir.utf8().get_data()); - if (aad) { - - current_dir = new_dir; - AAssetDir_close(aad); - return OK; - } - - return ERR_INVALID_PARAMETER; -} - -String DirAccessAndroid::get_current_dir() { - - return "/" + current_dir; -} - -bool DirAccessAndroid::file_exists(String p_file) { - - String sd; - if (current_dir == "") - sd = p_file; - else - sd = current_dir + "/" + p_file; - - AAsset *a = AAssetManager_open(FileAccessAndroid::asset_manager, sd.utf8().get_data(), AASSET_MODE_STREAMING); - if (a) { - AAsset_close(a); - return true; - } - - return false; -} - -Error DirAccessAndroid::make_dir(String p_dir) { - - ERR_FAIL_V(ERR_UNAVAILABLE); -} - -Error DirAccessAndroid::rename(String p_from, String p_to) { - - ERR_FAIL_V(ERR_UNAVAILABLE); -} - -Error DirAccessAndroid::remove(String p_name) { - - ERR_FAIL_V(ERR_UNAVAILABLE); -} - -//FileType get_file_type() const; -size_t DirAccessAndroid::get_space_left() { - - return 0; -} - -void DirAccessAndroid::make_default() { - - instance_func = create_fs; -} - -DirAccessAndroid::DirAccessAndroid() { - - aad = NULL; -} - -DirAccessAndroid::~DirAccessAndroid() { - - list_dir_end(); -} -#endif diff --git a/platform/android/dir_access_android.h b/platform/android/dir_access_android.h deleted file mode 100644 index 3ac0bd6332..0000000000 --- a/platform/android/dir_access_android.h +++ /dev/null @@ -1,80 +0,0 @@ -/*************************************************************************/ -/* dir_access_android.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef DIR_ACCESS_ANDROID_H -#define DIR_ACCESS_ANDROID_H - -#ifdef ANDROID_NATIVE_ACTIVITY - -#include "core/os/dir_access.h" -#include <android/asset_manager.h> -#include <android/log.h> -#include <android_native_app_glue.h> -#include <stdio.h> - -class DirAccessAndroid : public DirAccess { - - AAssetDir *aad; - String current_dir; - String current; - - static DirAccess *create_fs(); - -public: - virtual Error list_dir_begin(); ///< This starts dir listing - virtual String get_next(); - virtual bool current_is_dir() const; - virtual bool current_is_hidden() const; - virtual void list_dir_end(); ///< - - virtual int get_drive_count(); - virtual String get_drive(int p_drive); - - virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success - virtual String get_current_dir(); ///< return current dir location - - virtual bool file_exists(String p_file); - - virtual Error make_dir(String p_dir); - - virtual Error rename(String p_from, String p_to); - virtual Error remove(String p_name); - - //virtual FileType get_file_type() const; - size_t get_space_left(); - - static void make_default(); - - DirAccessAndroid(); - ~DirAccessAndroid(); -}; - -#endif -#endif // DIR_ACCESS_ANDROID_H diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 6a95277585..679a13217f 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANDROID_NATIVE_ACTIVITY - #include "dir_access_jandroid.h" #include "core/print_string.h" #include "file_access_jandroid.h" @@ -245,4 +243,3 @@ DirAccessJAndroid::~DirAccessJAndroid() { list_dir_end(); } -#endif diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index 1653fb0aa5..ea1e11a4f1 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -31,8 +31,6 @@ #ifndef DIR_ACCESS_JANDROID_H #define DIR_ACCESS_JANDROID_H -#ifndef ANDROID_NATIVE_ACTIVITY - #include "core/os/dir_access.h" #include "java_glue.h" #include <stdio.h> @@ -87,4 +85,3 @@ public: }; #endif // DIR_ACCESS_JANDROID_H -#endif diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 3b503e2657..a3b5b6dd58 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -395,7 +395,7 @@ class EditorExportAndroid : public EditorExportPlatform { return aname; } - String get_package_name(const String &p_package) { + String get_package_name(const String &p_package) const { String pname = p_package; String basename = ProjectSettings::get_singleton()->get("application/config/name"); @@ -420,6 +420,70 @@ class EditorExportAndroid : public EditorExportPlatform { return pname; } + bool is_package_name_valid(const String &p_package, String *r_error = NULL) const { + + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = "Package name is missing."; + } + return false; + } + + int segments = 0; + bool first = true; + for (int i = 0; i < pname.length(); i++) { + CharType c = pname[i]; + if (first && c == '.') { + if (r_error) { + *r_error = "Package segments must be of non-zero length."; + } + return false; + } + if (c == '.') { + segments++; + first = true; + continue; + } + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { + if (r_error) { + *r_error = "The character '" + String::chr(c) + "' is not allowed in Android application package names."; + } + return false; + } + if (first && (c >= '0' && c <= '9')) { + if (r_error) { + *r_error = "A digit cannot be the first character in a package segment."; + } + return false; + } + if (first && c == '_') { + if (r_error) { + *r_error = "The character '" + String::chr(c) + "' cannot be the first character in a package segment."; + } + return false; + } + first = false; + } + + if (segments == 0) { + if (r_error) { + *r_error = "The package must have at least one '.' separator."; + } + return false; + } + + if (first) { + if (r_error) { + *r_error = "Package segments must be of non-zero length."; + } + return false; + } + + return true; + } + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { /* @@ -608,10 +672,13 @@ class EditorExportAndroid : public EditorExportPlatform { aperms++; } - for (int i = 0; i < MAX_USER_PERMISSIONS; i++) { - String user_perm = p_preset->get("user_permissions/" + itos(i)); - if (user_perm.strip_edges() != "" && user_perm.strip_edges() != "False") - perms.push_back(user_perm.strip_edges()); + PoolStringArray user_perms = p_preset->get("permissions/custom_permissions"); + + for (int i = 0; i < user_perms.size(); i++) { + String user_perm = user_perms[i].strip_edges(); + if (!user_perm.empty()) { + perms.push_back(user_perm); + } } if (p_give_internet) { @@ -1040,10 +1107,6 @@ class EditorExportAndroid : public EditorExportPlatform { } public: - enum { - MAX_USER_PERMISSIONS = 20 - }; - typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total); public: @@ -1105,17 +1168,14 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); } + r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "permissions/custom_permissions"), PoolStringArray())); + const char **perms = android_perms; while (*perms) { r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false)); perms++; } - - for (int i = 0; i < MAX_USER_PERMISSIONS; i++) { - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "user_permissions/" + itos(i)), false)); - } } virtual String get_name() const { @@ -1382,12 +1442,23 @@ public: } } + String pn = p_preset->get("package/unique_name"); + String pn_err; + + if (!is_package_name_valid(get_package_name(pn), &pn_err)) { + + valid = false; + err += "Invalid package name - " + pn_err + "\n"; + } + r_error = err; return valid; } - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { - return "apk"; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("apk"); + return list; } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index 573200bcf9..bba45ffc1d 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANDROID_NATIVE_ACTIVITY - #include "file_access_jandroid.h" #include "core/os/os.h" #include "thread_jandroid.h" @@ -212,5 +210,3 @@ FileAccessJAndroid::~FileAccessJAndroid() { if (is_open()) close(); } - -#endif diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index 39c201ba85..98486702ab 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -31,8 +31,6 @@ #ifndef FILE_ACCESS_JANDROID_H #define FILE_ACCESS_JANDROID_H -#ifndef ANDROID_NATIVE_ACTIVITY - #include "core/os/file_access.h" #include "java_glue.h" class FileAccessJAndroid : public FileAccess { @@ -81,6 +79,4 @@ public: ~FileAccessJAndroid(); }; -#endif - #endif // FILE_ACCESS_JANDROID_H diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp deleted file mode 100644 index c46c6f7804..0000000000 --- a/platform/android/godot_android.cpp +++ /dev/null @@ -1,937 +0,0 @@ -/*************************************************************************/ -/* godot_android.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifdef ANDROID_NATIVE_ACTIVITY - -#include "core/engine.h" -#include "core/project_settings.h" -#include "file_access_android.h" -#include "main/main.h" -#include "os_android.h" - -#include <EGL/egl.h> -#include <android/log.h> -#include <android/sensor.h> -#include <android/window.h> -#include <android_native_app_glue.h> -#include <errno.h> -#include <jni.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "godot", __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "godot", __VA_ARGS__)) - -extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerSingleton(JNIEnv *env, jobject obj, jstring name, jobject p_object); -JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); -JNIEXPORT jstring JNICALL Java_org_godotengine_godot_Godot_getGlobal(JNIEnv *env, jobject obj, jstring path); -}; - -class JNISingleton : public Object { - - GDCLASS(JNISingleton, Object); - - struct MethodData { - - jmethodID method; - Variant::Type ret_type; - Vector<Variant::Type> argtypes; - }; - - jobject instance; - Map<StringName, MethodData> method_map; - JNIEnv *env; - -public: - void update_env(JNIEnv *p_env) { env = p_env; } - - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - - r_error.error = Variant::CallError::CALL_OK; - - Map<StringName, MethodData>::Element *E = method_map.find(p_method); - if (!E) { - - r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - - int ac = E->get().argtypes.size(); - if (ac < p_argcount) { - - r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - if (ac > p_argcount) { - - r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = ac; - return Variant(); - } - - for (int i = 0; i < p_argcount; i++) { - - if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) { - - r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = i; - r_error.expected = E->get().argtypes[i]; - } - } - - jvalue *v = NULL; - - if (p_argcount) { - - v = (jvalue *)alloca(sizeof(jvalue) * p_argcount); - } - - for (int i = 0; i < p_argcount; i++) { - - switch (E->get().argtypes[i]) { - - case Variant::BOOL: { - - v[i].z = *p_args[i]; - } break; - case Variant::INT: { - - v[i].i = *p_args[i]; - } break; - case Variant::REAL: { - - v[i].f = *p_args[i]; - } break; - case Variant::STRING: { - - String s = *p_args[i]; - jstring jStr = env->NewStringUTF(s.utf8().get_data()); - v[i].l = jStr; - } break; - case Variant::STRING_ARRAY: { - - PoolVector<String> sarray = *p_args[i]; - jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); - - for (int j = 0; j < sarray.size(); j++) { - - env->SetObjectArrayElement(arr, j, env->NewStringUTF(sarray[i].utf8().get_data())); - } - v[i].l = arr; - - } break; - case Variant::INT_ARRAY: { - - PoolVector<int> array = *p_args[i]; - jintArray arr = env->NewIntArray(array.size()); - PoolVector<int>::Read r = array.read(); - env->SetIntArrayRegion(arr, 0, array.size(), r.ptr()); - v[i].l = arr; - - } break; - case Variant::REAL_ARRAY: { - - PoolVector<float> array = *p_args[i]; - jfloatArray arr = env->NewFloatArray(array.size()); - PoolVector<float>::Read r = array.read(); - env->SetFloatArrayRegion(arr, 0, array.size(), r.ptr()); - v[i].l = arr; - - } break; - default: { - - ERR_FAIL_V(Variant()); - } break; - } - } - - Variant ret; - - switch (E->get().ret_type) { - - case Variant::NIL: { - - env->CallVoidMethodA(instance, E->get().method, v); - } break; - case Variant::BOOL: { - - ret = env->CallBooleanMethodA(instance, E->get().method, v); - } break; - case Variant::INT: { - - ret = env->CallIntMethodA(instance, E->get().method, v); - } break; - case Variant::REAL: { - - ret = env->CallFloatMethodA(instance, E->get().method, v); - } break; - case Variant::STRING: { - - jobject o = env->CallObjectMethodA(instance, E->get().method, v); - String singname = env->GetStringUTFChars((jstring)o, NULL); - } break; - case Variant::STRING_ARRAY: { - - jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v); - - int stringCount = env->GetArrayLength(arr); - PoolVector<String> sarr; - - for (int i = 0; i < stringCount; i++) { - jstring string = (jstring)env->GetObjectArrayElement(arr, i); - const char *rawString = env->GetStringUTFChars(string, 0); - sarr.push_back(String(rawString)); - } - - ret = sarr; - - } break; - case Variant::INT_ARRAY: { - - jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - PoolVector<int> sarr; - sarr.resize(fCount); - - PoolVector<int>::Write w = sarr.write(); - env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector<int>::Write(); - ret = sarr; - } break; - case Variant::REAL_ARRAY: { - - jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v); - - int fCount = env->GetArrayLength(arr); - PoolVector<float> sarr; - sarr.resize(fCount); - - PoolVector<float>::Write w = sarr.write(); - env->GetFloatArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector<float>::Write(); - ret = sarr; - } break; - default: { - - ERR_FAIL_V(Variant()); - } break; - } - - return ret; - } - - jobject get_instance() const { - - return instance; - } - void set_instance(jobject p_instance) { - - instance = p_instance; - } - - void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) { - - MethodData md; - md.method = p_method; - md.argtypes = p_args; - md.ret_type = p_ret_type; - method_map[p_name] = md; - } - - JNISingleton() {} -}; - -//JNIEnv *JNISingleton::env=NULL; - -static HashMap<String, JNISingleton *> jni_singletons; - -struct engine { - struct android_app *app; - OS_Android *os; - JNIEnv *jni; - - ASensorManager *sensorManager; - const ASensor *accelerometerSensor; - const ASensor *magnetometerSensor; - const ASensor *gyroscopeSensor; - ASensorEventQueue *sensorEventQueue; - - bool display_active; - bool requested_quit; - int animating; - EGLDisplay display; - EGLSurface surface; - EGLContext context; - int32_t width; - int32_t height; -}; - -/** - * Initialize an EGL context for the current display. - */ -static int engine_init_display(struct engine *engine, bool p_gl2) { - // initialize OpenGL ES and EGL - - /* - * Here specify the attributes of the desired configuration. - * Below, we select an EGLConfig with at least 8 bits per color - * component compatible with on-screen windows - */ - const EGLint gl2_attribs[] = { - // EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_BLUE_SIZE, 4, - EGL_GREEN_SIZE, 4, - EGL_RED_SIZE, 4, - EGL_ALPHA_SIZE, 0, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, EGL_DONT_CARE, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - const EGLint gl1_attribs[] = { - // EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_BLUE_SIZE, 4, - EGL_GREEN_SIZE, 4, - EGL_RED_SIZE, 4, - EGL_ALPHA_SIZE, 0, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, EGL_DONT_CARE, - EGL_NONE - }; - - const EGLint *attribs = p_gl2 ? gl2_attribs : gl1_attribs; - - EGLint w, h, dummy, format; - EGLint numConfigs; - EGLConfig config; - EGLSurface surface; - EGLContext context; - - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - - eglInitialize(display, 0, 0); - - /* Here, the application chooses the configuration it desires. In this - * sample, we have a very simplified selection process, where we pick - * the first EGLConfig that matches our criteria */ - - eglChooseConfig(display, attribs, &config, 1, &numConfigs); - - LOGI("Num configs: %i\n", numConfigs); - - /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is - * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). - * As soon as we picked a EGLConfig, we can safely reconfigure the - * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ - eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); - - ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); - //ANativeWindow_setFlags(engine->app->window, 0, 0, format|); - - surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); - - const EGLint context_attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - context = eglCreateContext(display, config, EGL_NO_CONTEXT, p_gl2 ? context_attribs : NULL); - - if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { - LOGW("Unable to eglMakeCurrent"); - return -1; - } - - eglQuerySurface(display, surface, EGL_WIDTH, &w); - eglQuerySurface(display, surface, EGL_HEIGHT, &h); - - //engine->os->set_egl_extensions(eglQueryString(display,EGL_EXTENSIONS)); - engine->os->init_video_mode(w, h); - - engine->display = display; - engine->context = context; - engine->surface = surface; - engine->width = w; - engine->height = h; - engine->display_active = true; - - //engine->state.angle = 0; - - // Initialize GL state. - //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); - glEnable(GL_CULL_FACE); - // glShadeModel(GL_SMOOTH); - glDisable(GL_DEPTH_TEST); - LOGI("GL Version: %s - %s %s\n", glGetString(GL_VERSION), glGetString(GL_VENDOR), glGetString(GL_RENDERER)); - - return 0; -} - -static void engine_draw_frame(struct engine *engine) { - if (engine->display == NULL) { - // No display. - return; - } - - // Just fill the screen with a color. - //glClearColor(0,1,0,1); - //glClear(GL_COLOR_BUFFER_BIT); - if (engine->os && engine->os->main_loop_iterate()) { - - engine->requested_quit = true; - return; //should exit instead - } - - eglSwapBuffers(engine->display, engine->surface); -} - -static void engine_term_display(struct engine *engine) { - if (engine->display != EGL_NO_DISPLAY) { - eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (engine->context != EGL_NO_CONTEXT) { - eglDestroyContext(engine->display, engine->context); - } - if (engine->surface != EGL_NO_SURFACE) { - eglDestroySurface(engine->display, engine->surface); - } - eglTerminate(engine->display); - } - - engine->animating = 0; - engine->display = EGL_NO_DISPLAY; - engine->context = EGL_NO_CONTEXT; - engine->surface = EGL_NO_SURFACE; - engine->display_active = false; -} - -/** - * Process the next input event. - */ -static int32_t engine_handle_input(struct android_app *app, AInputEvent *event) { - struct engine *engine = (struct engine *)app->userData; - - if (!engine->os) - return 0; - - switch (AInputEvent_getType(event)) { - - case AINPUT_EVENT_TYPE_KEY: { - - int ac = AKeyEvent_getAction(event); - switch (ac) { - - case AKEY_EVENT_ACTION_DOWN: { - - int32_t code = AKeyEvent_getKeyCode(event); - if (code == AKEYCODE_BACK) { - - //AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); - if (engine->os) - engine->os->main_loop_request_quit(); - return 1; - } - - } break; - case AKEY_EVENT_ACTION_UP: { - - } break; - } - - } break; - case AINPUT_EVENT_TYPE_MOTION: { - - Vector<OS_Android::TouchPos> touchvec; - - int pc = AMotionEvent_getPointerCount(event); - - touchvec.resize(pc); - - for (int i = 0; i < pc; i++) { - - touchvec[i].pos.x = AMotionEvent_getX(event, i); - touchvec[i].pos.y = AMotionEvent_getY(event, i); - touchvec[i].id = AMotionEvent_getPointerId(event, i); - } - - //System.out.printf("gaction: %d\n",event.getAction()); - int pidx = (AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> 8; - switch (AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK) { - - case AMOTION_EVENT_ACTION_DOWN: { - engine->os->process_touch(0, 0, touchvec); - - //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); - } break; - case AMOTION_EVENT_ACTION_MOVE: { - engine->os->process_touch(1, 0, touchvec); - /* - for(int i=0;i<event.getPointerCount();i++) { - System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); - } - */ - } break; - case AMOTION_EVENT_ACTION_POINTER_UP: { - - engine->os->process_touch(4, pidx, touchvec); - //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); - } break; - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - engine->os->process_touch(3, pidx, touchvec); - //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); - } break; - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_UP: { - engine->os->process_touch(2, 0, touchvec); - /* - for(int i=0;i<event.getPointerCount();i++) { - System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); - } - */ - } break; - } - - return 1; - } break; - } - - return 0; -} - -/** - * Process the next main command. - */ - -static void _gfx_init(void *ud, bool p_gl2) { - - struct engine *engine = (struct engine *)ud; - engine_init_display(engine, p_gl2); -} - -static void engine_handle_cmd(struct android_app *app, int32_t cmd) { - struct engine *engine = (struct engine *)app->userData; - // LOGI("**** CMD %i\n",cmd); - switch (cmd) { - case APP_CMD_SAVE_STATE: - // The system has asked us to save our current state. Do so. - //engine->app->savedState = malloc(sizeof(struct saved_state)); - //*((struct saved_state*)engine->app->savedState) = engine->state; - //engine->app->savedStateSize = sizeof(struct saved_state); - break; - case APP_CMD_CONFIG_CHANGED: - case APP_CMD_WINDOW_RESIZED: { - - if (engine->display_active) { - - EGLint w, h; - eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); - eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); - // if (w==engine->os->get_video_mode().width && h==engine->os->get_video_mode().height) - // break; - - engine_term_display(engine); - } - - engine->os->reload_gfx(); - engine_draw_frame(engine); - engine->animating = 1; - - } break; - case APP_CMD_INIT_WINDOW: - //The window is being shown, get it ready. - //LOGI("INIT WINDOW"); - if (engine->app->window != NULL) { - - if (engine->os == NULL) { - - //do initialization here, when there's OpenGL! hackish but the only way - engine->os = new OS_Android(_gfx_init, engine); - - __android_log_print(ANDROID_LOG_INFO, "godot", "pre asdasd setup..."); - - Error err = Main::setup("apk", 0, NULL); - - String modules = ProjectSettings::get_singleton()->get("android/modules"); - Vector<String> mods = modules.split(",", false); - mods.push_back("GodotOS"); - __android_log_print(ANDROID_LOG_INFO, "godot", "mod count: %i", mods.size()); - - if (mods.size()) { - - jclass activityClass = engine->jni->FindClass("android/app/NativeActivity"); - - jmethodID getClassLoader = engine->jni->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); - - jobject cls = engine->jni->CallObjectMethod(app->activity->clazz, getClassLoader); - - jclass classLoader = engine->jni->FindClass("java/lang/ClassLoader"); - - jmethodID findClass = engine->jni->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - - static JNINativeMethod methods[] = { - { "registerSingleton", "(Ljava/lang/String;Ljava/lang/Object;)V", (void *)&Java_org_godotengine_godot_Godot_registerSingleton }, - { "registerMethod", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V", (void *)&Java_org_godotengine_godot_Godot_registerMethod }, - { "getGlobal", "(Ljava/lang/String;)Ljava/lang/String;", (void *)&Java_org_godotengine_godot_Godot_getGlobal }, - }; - - jstring gstrClassName = engine->jni->NewStringUTF("org/godotengine/godot/Godot"); - jclass GodotClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, gstrClassName); - - __android_log_print(ANDROID_LOG_INFO, "godot", "godot ****^*^*?^*^*class data %x", GodotClass); - - engine->jni->RegisterNatives(GodotClass, methods, sizeof(methods) / sizeof(methods[0])); - - for (int i = 0; i < mods.size(); i++) { - - String m = mods[i]; - //jclass singletonClass = engine->jni->FindClass(m.utf8().get_data()); - - jstring strClassName = engine->jni->NewStringUTF(m.utf8().get_data()); - jclass singletonClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, strClassName); - - __android_log_print(ANDROID_LOG_INFO, "godot", "****^*^*?^*^*class data %x", singletonClass); - jmethodID initialize = engine->jni->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lorg/godotengine/godot/Godot$SingletonBase;"); - - jobject obj = engine->jni->CallStaticObjectMethod(singletonClass, initialize, app->activity->clazz); - __android_log_print(ANDROID_LOG_INFO, "godot", "****^*^*?^*^*class instance %x", obj); - jobject gob = engine->jni->NewGlobalRef(obj); - } - } - - if (!Main::start()) - return; //should exit instead and print the error - - engine->os->main_loop_begin(); - } else { - //i guess recreate resources? - engine->os->reload_gfx(); - } - - engine->animating = 1; - engine_draw_frame(engine); - } - break; - case APP_CMD_TERM_WINDOW: - // The window is being hidden or closed, clean it up. - //LOGI("TERM WINDOW"); - engine_term_display(engine); - break; - case APP_CMD_GAINED_FOCUS: - // When our app gains focus, we start monitoring the accelerometer. - if (engine->accelerometerSensor != NULL) { - ASensorEventQueue_enableSensor(engine->sensorEventQueue, - engine->accelerometerSensor); - // We'd like to get 60 events per second (in us). - ASensorEventQueue_setEventRate(engine->sensorEventQueue, - engine->accelerometerSensor, (1000L / 60) * 1000); - } - // start monitoring gravity - if (engine->gravitySensor != NULL) { - ASensorEventQueue_enableSensor(engine->sensorEventQueue, - engine->gravitySensor); - // We'd like to get 60 events per second (in us). - ASensorEventQueue_setEventRate(engine->sensorEventQueue, - engine->gravitySensor, (1000L / 60) * 1000); - } - // Also start monitoring the magnetometer. - if (engine->magnetometerSensor != NULL) { - ASensorEventQueue_enableSensor(engine->sensorEventQueue, - engine->magnetometerSensor); - // We'd like to get 60 events per second (in us). - ASensorEventQueue_setEventRate(engine->sensorEventQueue, - engine->magnetometerSensor, (1000L / 60) * 1000); - } - // And the gyroscope. - if (engine->gyroscopeSensor != NULL) { - ASensorEventQueue_enableSensor(engine->sensorEventQueue, - engine->gyroscopeSensor); - // We'd like to get 60 events per second (in us). - ASensorEventQueue_setEventRate(engine->sensorEventQueue, - engine->gyroscopeSensor, (1000L / 60) * 1000); - } - engine->animating = 1; - break; - case APP_CMD_LOST_FOCUS: - // When our app loses focus, we stop monitoring the sensors. - // This is to avoid consuming battery while not being used. - if (engine->accelerometerSensor != NULL) { - ASensorEventQueue_disableSensor(engine->sensorEventQueue, - engine->accelerometerSensor); - } - if (engine->gravitySensor != NULL) { - ASensorEventQueue_disableSensor(engine->sensorEventQueue, - engine->gravitySensor); - } - if (engine->magnetometerSensor != NULL) { - ASensorEventQueue_disableSensor(engine->sensorEventQueue, - engine->magnetometerSensor); - } - if (engine->gyroscopeSensor != NULL) { - ASensorEventQueue_disableSensor(engine->sensorEventQueue, - engine->gyroscopeSensor); - } - // Also stop animating. - engine->animating = 0; - engine_draw_frame(engine); - break; - } -} - -void android_main(struct android_app *app) { - struct engine engine; - // Make sure glue isn't stripped. - app_dummy(); - - memset(&engine, 0, sizeof(engine)); - app->userData = &engine; - app->onAppCmd = engine_handle_cmd; - app->onInputEvent = engine_handle_input; - engine.app = app; - engine.requested_quit = false; - engine.os = NULL; - engine.display_active = false; - - FileAccessAndroid::asset_manager = app->activity->assetManager; - - // Prepare to monitor sensors - engine.sensorManager = ASensorManager_getInstance(); - engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, - ASENSOR_TYPE_ACCELEROMETER); - engine.gravitySensor = ASensorManager_getDefaultSensor(engine.sensorManager, - ASENSOR_TYPE_GRAVITY); - engine.magnetometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, - ASENSOR_TYPE_MAGNETIC_FIELD); - engine.gyroscopeSensor = ASensorManager_getDefaultSensor(engine.sensorManager, - ASENSOR_TYPE_GYROSCOPE); - engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, - app->looper, LOOPER_ID_USER, NULL, NULL); - - ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN | AWINDOW_FLAG_KEEP_SCREEN_ON, 0); - - app->activity->vm->AttachCurrentThread(&engine.jni, NULL); - - // loop waiting for stuff to do. - - while (1) { - // Read all pending events. - int ident; - int events; - struct android_poll_source *source; - - // If not animating, we will block forever waiting for events. - // If animating, we loop until all events are read, then continue - // to draw the next frame of animation. - - int nullmax = 50; - while ((ident = ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, - (void **)&source)) >= 0) { - - // Process this event. - - if (source != NULL) { - // LOGI("process\n"); - source->process(app, source); - } else { - nullmax--; - if (nullmax < 0) - break; - } - - // If a sensor has data, process it now. - // LOGI("events\n"); - if (ident == LOOPER_ID_USER) { - if (engine.accelerometerSensor != NULL || engine.magnetometerSensor != NULL || engine.gyroscopeSensor != NULL) { - ASensorEvent event; - while (ASensorEventQueue_getEvents(engine.sensorEventQueue, - &event, 1) > 0) { - - if (engine.os) { - if (event.acceleration != NULL) { - engine.os->process_accelerometer(Vector3(event.acceleration.x, event.acceleration.y, - event.acceleration.z)); - } - if (event.magnetic != NULL) { - engine.os->process_magnetometer(Vector3(event.magnetic.x, event.magnetic.y, - event.magnetic.z)); - } - if (event.vector != NULL) { - engine.os->process_gyroscope(Vector3(event.vector.x, event.vector.y, - event.vector.z)); - } - } - } - } - } - - // Check if we are exiting. - if (app->destroyRequested != 0) { - if (engine.os) { - engine.os->main_loop_request_quit(); - } - app->destroyRequested = 0; - } - - if (engine.requested_quit) { - engine_term_display(&engine); - exit(0); - } - - // LOGI("end\n"); - } - - // LOGI("engine animating? %i\n",engine.animating); - - if (engine.animating) { - //do os render - - engine_draw_frame(&engine); - //LOGI("TERM WINDOW"); - } - } -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerSingleton(JNIEnv *env, jobject obj, jstring name, jobject p_object) { - - String singname = env->GetStringUTFChars(name, NULL); - JNISingleton *s = memnew(JNISingleton); - s->update_env(env); - s->set_instance(env->NewGlobalRef(p_object)); - jni_singletons[singname] = s; - - Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); -} - -static Variant::Type get_jni_type(const String &p_type) { - - static struct { - const char *name; - Variant::Type type; - } _type_to_vtype[] = { - { "void", Variant::NIL }, - { "boolean", Variant::BOOL }, - { "int", Variant::INT }, - { "float", Variant::REAL }, - { "java.lang.String", Variant::STRING }, - { "[I", Variant::INT_ARRAY }, - { "[F", Variant::REAL_ARRAY }, - { "[Ljava.lang.String;", Variant::STRING_ARRAY }, - { NULL, Variant::NIL } - }; - - int idx = 0; - - while (_type_to_vtype[idx].name) { - - if (p_type == _type_to_vtype[idx].name) - return _type_to_vtype[idx].type; - - idx++; - } - - return Variant::NIL; -} - -static const char *get_jni_sig(const String &p_type) { - - static struct { - const char *name; - const char *sig; - } _type_to_vtype[] = { - { "void", "V" }, - { "boolean", "Z" }, - { "int", "I" }, - { "float", "F" }, - { "java.lang.String", "Ljava/lang/String;" }, - { "[I", "[I" }, - { "[F", "[F" }, - { "[Ljava.lang.String;", "[Ljava/lang/String;" }, - { NULL, "V" } - }; - - int idx = 0; - - while (_type_to_vtype[idx].name) { - - if (p_type == _type_to_vtype[idx].name) - return _type_to_vtype[idx].sig; - - idx++; - } - - return ""; -} - -JNIEXPORT jstring JNICALL Java_org_godotengine_godot_Godot_getGlobal(JNIEnv *env, jobject obj, jstring path) { - - String js = env->GetStringUTFChars(path, NULL); - - return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) { - - String singname = env->GetStringUTFChars(sname, NULL); - - ERR_FAIL_COND(!jni_singletons.has(singname)); - - JNISingleton *s = jni_singletons.get(singname); - - String mname = env->GetStringUTFChars(name, NULL); - String retval = env->GetStringUTFChars(ret, NULL); - Vector<Variant::Type> types; - String cs = "("; - - int stringCount = env->GetArrayLength(args); - - for (int i = 0; i < stringCount; i++) { - - jstring string = (jstring)env->GetObjectArrayElement(args, i); - const char *rawString = env->GetStringUTFChars(string, 0); - types.push_back(get_jni_type(String(rawString))); - cs += get_jni_sig(String(rawString)); - } - - cs += ")"; - cs += get_jni_sig(retval); - jclass cls = env->GetObjectClass(s->get_instance()); - jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); - if (!mid) { - - print_line("RegisterMethod: Failed getting method ID: " + mname); - } - - s->add_method(mname, mid, types, get_jni_type(retval)); -} - -#endif diff --git a/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml b/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml index 23bac02294..104993da7e 100644 --- a/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml +++ b/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml @@ -32,9 +32,9 @@ android:id="@+id/appIcon" android:layout_width="fill_parent" android:layout_height="25dp" - android:scaleType="centerInside" + android:scaleType="centerInside" android:layout_alignParentLeft="true" - android:layout_alignParentTop="true" + android:layout_alignParentTop="true" android:src="@android:drawable/stat_sys_download" /> <TextView diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index ab37f7a02c..88194f00d1 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -279,7 +279,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC // ...add to FrameLayout layout.addView(edittext); - mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl,this); + mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl, this); layout.addView(mView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); edittext.setView(mView); io.setEdit(edittext); diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index 181ffc3b4b..4cb4db33de 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -86,7 +86,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { private Godot activity; private InputManagerCompat mInputManager; - public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl,Godot p_activity) { + public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl, Godot p_activity) { super(context); ctx = context; io = p_io; @@ -204,48 +204,65 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { return i; } } - onInputDeviceAdded(device_id); - return joy_devices.size() - 1; + + return -1; } @Override public void onInputDeviceAdded(int deviceId) { - joystick joy = new joystick(); - joy.device_id = deviceId; - final int id = joy_devices.size(); - InputDevice device = mInputManager.getInputDevice(deviceId); - final String name = device.getName(); - joy.name = device.getName(); - joy.axes = new ArrayList<InputDevice.MotionRange>(); - joy.hats = new ArrayList<InputDevice.MotionRange>(); - List<InputDevice.MotionRange> ranges = device.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); - for (InputDevice.MotionRange range : ranges) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joy.hats.add(range); - } else { - joy.axes.add(range); + int id = find_joy_device(deviceId); + + // Check if the device has not been already added + if (id < 0) { + InputDevice device = mInputManager.getInputDevice(deviceId); + + id = joy_devices.size(); + + joystick joy = new joystick(); + joy.device_id = deviceId; + joy.name = device.getName(); + joy.axes = new ArrayList<InputDevice.MotionRange>(); + joy.hats = new ArrayList<InputDevice.MotionRange>(); + + List<InputDevice.MotionRange> ranges = device.getMotionRanges(); + Collections.sort(ranges, new RangeComparator()); + + for (InputDevice.MotionRange range : ranges) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { + joy.hats.add(range); + } else { + joy.axes.add(range); + } } + + joy_devices.add(joy); + + final int device_id = id; + final String name = joy.name; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, true, name); + } + }); } - joy_devices.add(joy); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(id, true, name); - } - }); } @Override public void onInputDeviceRemoved(int deviceId) { - final int id = find_joy_device(deviceId); - joy_devices.remove(id); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(id, false, ""); - } - }); + final int device_id = find_joy_device(deviceId); + + // Check if the evice has not been already removed + if (device_id > -1) { + joy_devices.remove(device_id); + + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, false, ""); + } + }); + } } @Override @@ -266,15 +283,18 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { final int button = get_godot_button(keyCode); - final int device = find_joy_device(event.getDeviceId()); + final int device_id = find_joy_device(event.getDeviceId()); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joybutton(device, button, false); - } - }); - return true; + // Check if the device exists + if (device_id > -1) { + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device_id, button, false); + } + }); + return true; + } } else { final int chr = event.getUnicodeChar(0); queueEvent(new Runnable() { @@ -284,6 +304,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } }); }; + return super.onKeyUp(keyCode, event); }; @@ -308,18 +329,20 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if (event.getRepeatCount() > 0) // ignore key echo return true; - final int button = get_godot_button(keyCode); - final int device = find_joy_device(event.getDeviceId()); - //Log.e(TAG, String.format("joy button down! button %x, %d, device %d", keyCode, button, device)); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joybutton(device, button, true); - } - }); - return true; + final int button = get_godot_button(keyCode); + final int device_id = find_joy_device(event.getDeviceId()); + // Check if the device exists + if (device_id > -1) { + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device_id, button, true); + } + }); + return true; + } } else { final int chr = event.getUnicodeChar(0); queueEvent(new Runnable() { @@ -329,6 +352,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } }); }; + return super.onKeyDown(keyCode, event); } @@ -338,33 +362,35 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { final int device_id = find_joy_device(event.getDeviceId()); - joystick joy = joy_devices.get(device_id); - for (int i = 0; i < joy.axes.size(); i++) { - InputDevice.MotionRange range = joy.axes.get(i); - final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; - //Log.e(TAG, String.format("axis event: %d, value %f", i, value)); - final int idx = i; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyaxis(device_id, idx, value); - } - }); - } + // Check if the device exists + if (device_id > -1) { + joystick joy = joy_devices.get(device_id); + + for (int i = 0; i < joy.axes.size(); i++) { + InputDevice.MotionRange range = joy.axes.get(i); + final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; + final int idx = i; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyaxis(device_id, idx, value); + } + }); + } - for (int i = 0; i < joy.hats.size(); i += 2) { - final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); - final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); - //Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY)); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyhat(device_id, hatX, hatY); - } - }); + for (int i = 0; i < joy.hats.size(); i += 2) { + final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); + final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyhat(device_id, hatX, hatY); + } + }); + } + return true; } - return true; }; return super.onGenericMotionEvent(event); @@ -408,9 +434,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { setRenderer(new Renderer()); } - private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC; - private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR= 0x00000001; - + private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC; + private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001; + private static class ContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { @@ -422,9 +448,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { checkEglError("Before eglCreateContext", egl); EGLContext context; if (use_debug_opengl) { - int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2,_EGL_CONTEXT_FLAGS_KHR,_EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; - int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3,_EGL_CONTEXT_FLAGS_KHR,_EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; - context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); + int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; + int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); } else { int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; diff --git a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java index 7216d8b5a4..03a7a71bb1 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java +++ b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java @@ -44,7 +44,7 @@ import javax.net.ssl.TrustManagerFactory; import org.apache.http.conn.ssl.SSLSocketFactory; /** - * + * * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class CustomSSLSocketFactory extends SSLSocketFactory { diff --git a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java index b84f5cce2e..cfe9c4fef0 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java +++ b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java @@ -69,7 +69,7 @@ import android.content.SharedPreferences; import android.util.Log; /** - * + * * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class HttpRequester { diff --git a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java index 2368766afa..a1d5b26b3c 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java +++ b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java @@ -39,7 +39,7 @@ import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; /** - * + * * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class RequestParams { diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 07e4048c12..fb9c0f08ad 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANDROID_NATIVE_ACTIVITY - #include "java_glue.h" #include "android/asset_manager_jni.h" #include "audio_driver_jandroid.h" @@ -268,11 +266,11 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { return ret; }; - if (name == "java.lang.Integer") { + if (name == "java.lang.Integer" || name == "java.lang.Long") { jclass nclass = env->FindClass("java/lang/Number"); - jmethodID intValue = env->GetMethodID(nclass, "intValue", "()I"); - int ret = env->CallIntMethod(obj, intValue); + jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J"); + jlong ret = env->CallLongMethod(obj, longValue); return ret; }; @@ -1566,4 +1564,3 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * //Main::cleanup(); //return os.get_exit_code(); -#endif diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h index d433b5f0d8..dc5b9cca49 100644 --- a/platform/android/java_glue.h +++ b/platform/android/java_glue.h @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANDROID_NATIVE_ACTIVITY - #ifndef JAVA_GLUE_H #define JAVA_GLUE_H @@ -64,5 +62,4 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height); } -#endif #endif // JAVA_GLUE_H diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 8e050c1d27..afdd108987 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -39,15 +39,10 @@ #include "file_access_android.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" -//#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/visual_server_wrap_mt.h" -#ifdef ANDROID_NATIVE_ACTIVITY -#include "dir_access_android.h" -#include "file_access_android.h" -#else #include "dir_access_jandroid.h" #include "file_access_jandroid.h" -#endif #include <dlfcn.h> @@ -90,18 +85,6 @@ void OS_Android::initialize_core() { OS_Unix::initialize_core(); -#ifdef ANDROID_NATIVE_ACTIVITY - - FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES); - FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); - FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM); - //FileAccessBufferedFA<FileAccessUnix>::make_default(); - DirAccess::make_default<DirAccessAndroid>(DirAccess::ACCESS_RESOURCES); - DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); - DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); - -#else - if (use_apk_expansion) FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES); else { @@ -121,8 +104,6 @@ void OS_Android::initialize_core() { DirAccess::make_default<DirAccessJAndroid>(DirAccess::ACCESS_RESOURCES); DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); - -#endif } void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { @@ -183,13 +164,11 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int video_driver_index = p_video_driver; visual_server = memnew(VisualServerRaster); - /* if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - + if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { visual_server = memnew(VisualServerWrapMT(visual_server, false)); - };*/ + } visual_server->init(); - // visual_server->cursor_set_visible(false, 0); AudioDriverManager::initialize(p_audio_driver); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index e89a380e4c..ad6fe1976a 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -41,12 +41,6 @@ #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" -#ifdef ANDROID_NATIVE_ACTIVITY -#include <android/log.h> -#include <android/sensor.h> -#include <android_native_app_glue.h> -#endif - typedef void (*GFXInitFunc)(void *ud, bool gl2); typedef int (*OpenURIFunc)(const String &); typedef String (*GetUserDataDirFunc)(); diff --git a/platform/android/power_android.cpp b/platform/android/power_android.cpp index 51283183df..0a6bf9dfcb 100644 --- a/platform/android/power_android.cpp +++ b/platform/android/power_android.cpp @@ -98,7 +98,7 @@ ANativeWindow *Android_JNI_GetNativeWindow(void) { return anw; } -/* +/* * CODE CHUNK IMPORTED FROM SDL 2.0 * returns 0 on success or -1 on error (others undefined then) * returns truthy or falsy value in plugged, charged and battery diff --git a/platform/android/sign.sh b/platform/android/sign.sh deleted file mode 100755 index 830da05a37..0000000000 --- a/platform/android/sign.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore my-release-key.keystore "$1" reduz - -echo "" -echo "" -echo "Checking if APK is verified..." -jarsigner -verify "$1" -verbose -certs - |