From 79b946e8f2ccb552e1a3fe8222f660b9a76cc001 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 4 Aug 2010 11:13:03 -0700 Subject: [PATCH] Some native activity sample code cleanup. Update to include newest headers and library, tweak glue code to work better with state saving and add support for config changes. Change-Id: I4d27bd4a0f542f217efaec86cf4f219aca020426 --- .../usr/include/android/asset_manager.h | 10 - .../usr/include/android/asset_manager_jni.h | 40 +++ .../usr/include/android/configuration.h | 319 ++++++++++++++++++ .../arch-arm/usr/include/android/input.h | 95 +++++- .../usr/include/android/native_activity.h | 10 +- .../usr/include/android/native_window.h | 11 + .../arch-arm/usr/include/android/sensor.h | 45 ++- .../android-9/arch-arm/usr/lib/libandroid.so | Bin 17940 -> 26336 bytes .../native-activity/AndroidManifest.xml | 23 +- .../samples/native-activity/jni/main.c | 164 +++++---- .../samples/native-activity/src/Dummy.java | 4 + .../samples/native-plasma/jni/plasma.c | 86 +++-- .../samples/native-plasma/src/Dummy.java | 4 +- .../native_app_glue/android_native_app_glue.c | 182 +++++++++- .../native_app_glue/android_native_app_glue.h | 111 +++++- 15 files changed, 922 insertions(+), 182 deletions(-) create mode 100644 ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager_jni.h create mode 100644 ndk/platforms/android-9/arch-arm/usr/include/android/configuration.h create mode 100644 ndk/platforms/android-9/samples/native-activity/src/Dummy.java diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager.h b/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager.h index 89989f8cc..4fa0ef3a3 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager.h @@ -18,8 +18,6 @@ #ifndef ANDROID_ASSET_MANAGER_H #define ANDROID_ASSET_MANAGER_H -#include - #ifdef __cplusplus extern "C" { #endif @@ -42,14 +40,6 @@ enum { }; -/** - * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager - * object. Note that the caller is responsible for obtaining and holding a VM reference - * to the jobject to prevent its being garbage collected while the native object is - * in use. - */ -AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager); - /** * Open the named directory within the asset hierarchy. The directory can then * be inspected with the AAssetDir functions. To open the top-level directory, diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager_jni.h b/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager_jni.h new file mode 100644 index 000000000..aec2d3c97 --- /dev/null +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/asset_manager_jni.h @@ -0,0 +1,40 @@ +/* + * 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_ASSET_MANAGER_JNI_H +#define ANDROID_ASSET_MANAGER_JNI_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager + * object. Note that the caller is responsible for obtaining and holding a VM reference + * to the jobject to prevent its being garbage collected while the native object is + * in use. + */ +AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_ASSET_MANAGER_JNI_H diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/configuration.h b/ndk/platforms/android-9/arch-arm/usr/include/android/configuration.h new file mode 100644 index 000000000..79b9b1e30 --- /dev/null +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/configuration.h @@ -0,0 +1,319 @@ +/* + * 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_CONFIGURATION_H +#define ANDROID_CONFIGURATION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct AConfiguration; +typedef struct AConfiguration AConfiguration; + +enum { + ACONFIGURATION_ORIENTATION_ANY = 0x0000, + ACONFIGURATION_ORIENTATION_PORT = 0x0001, + ACONFIGURATION_ORIENTATION_LAND = 0x0002, + ACONFIGURATION_ORIENTATION_SQUARE = 0x0003, + + ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000, + ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001, + ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002, + ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003, + + ACONFIGURATION_DENSITY_DEFAULT = 0, + ACONFIGURATION_DENSITY_LOW = 120, + ACONFIGURATION_DENSITY_MEDIUM = 160, + ACONFIGURATION_DENSITY_HIGH = 240, + ACONFIGURATION_DENSITY_NONE = 0xffff, + + ACONFIGURATION_KEYBOARD_ANY = 0x0000, + ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001, + ACONFIGURATION_KEYBOARD_QWERTY = 0x0002, + ACONFIGURATION_KEYBOARD_12KEY = 0x0003, + + ACONFIGURATION_NAVIGATION_ANY = 0x0000, + ACONFIGURATION_NAVIGATION_NONAV = 0x0001, + ACONFIGURATION_NAVIGATION_DPAD = 0x0002, + ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003, + ACONFIGURATION_NAVIGATION_WHEEL = 0x0004, + + ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000, + ACONFIGURATION_KEYSHIDDEN_NO = 0x0001, + ACONFIGURATION_KEYSHIDDEN_YES = 0x0002, + ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003, + + ACONFIGURATION_NAVHIDDEN_ANY = 0x0000, + ACONFIGURATION_NAVHIDDEN_NO = 0x0001, + ACONFIGURATION_NAVHIDDEN_YES = 0x0002, + + ACONFIGURATION_SCREENSIZE_ANY = 0x00, + ACONFIGURATION_SCREENSIZE_SMALL = 0x01, + ACONFIGURATION_SCREENSIZE_NORMAL = 0x02, + ACONFIGURATION_SCREENSIZE_LARGE = 0x03, + ACONFIGURATION_SCREENSIZE_XLARGE = 0x04, + + ACONFIGURATION_SCREENLONG_ANY = 0x00, + ACONFIGURATION_SCREENLONG_NO = 0x1, + ACONFIGURATION_SCREENLONG_YES = 0x2, + + ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00, + ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01, + ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02, + ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03, + + ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00, + ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10, + ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20, + + ACONFIGURATION_MCC = 0x0001, + ACONFIGURATION_MNC = 0x0002, + ACONFIGURATION_LOCALE = 0x0004, + ACONFIGURATION_TOUCHSCREEN = 0x0008, + ACONFIGURATION_KEYBOARD = 0x0010, + ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020, + ACONFIGURATION_NAVIGATION = 0x0040, + ACONFIGURATION_ORIENTATION = 0x0080, + ACONFIGURATION_DENSITY = 0x0100, + ACONFIGURATION_SCREEN_SIZE = 0x0200, + ACONFIGURATION_VERSION = 0x0400, + ACONFIGURATION_SCREEN_LAYOUT = 0x0800, + ACONFIGURATION_UI_MODE = 0x1000, +}; + +/** + * Create a new AConfiguration, initialized with no values set. + */ +AConfiguration* AConfiguration_new(); + +/** + * Free an AConfiguration that was previously created with + * AConfiguration_new(). + */ +void AConfiguration_delete(AConfiguration* config); + +/** + * Create and return a new AConfiguration based on the current configuration in + * use in the given AssetManager. + */ +void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am); + +/** + * Copy the contents of 'src' to 'dest'. + */ +void AConfiguration_copy(AConfiguration* dest, AConfiguration* src); + +/** + * Return the current MCC set in the configuration. 0 if not set. + */ +int32_t AConfiguration_getMcc(AConfiguration* config); + +/** + * Set the current MCC in the configuration. 0 to clear. + */ +void AConfiguration_setMcc(AConfiguration* config, int32_t mcc); + +/** + * Return the current MNC set in the configuration. 0 if not set. + */ +int32_t AConfiguration_getMnc(AConfiguration* config); + +/** + * Set the current MNC in the configuration. 0 to clear. + */ +void AConfiguration_setMnc(AConfiguration* config, int32_t mnc); + +/** + * Return the current language code set in the configuration. The output will + * be filled with an array of two characters. They are not 0-terminated. If + * a language is not set, they will be 0. + */ +void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage); + +/** + * Set the current language code in the configuration, from the first two + * characters in the string. + */ +void AConfiguration_setLanguage(AConfiguration* config, const char* language); + +/** + * Return the current country code set in the configuration. The output will + * be filled with an array of two characters. They are not 0-terminated. If + * a country is not set, they will be 0. + */ +void AConfiguration_getCountry(AConfiguration* config, char* outCountry); + +/** + * Set the current country code in the configuration, from the first two + * characters in the string. + */ +void AConfiguration_setCountry(AConfiguration* config, const char* country); + +/** + * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration. + */ +int32_t AConfiguration_getOrientation(AConfiguration* config); + +/** + * Set the current orientation in the configuration. + */ +void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation); + +/** + * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration. + */ +int32_t AConfiguration_getTouchscreen(AConfiguration* config); + +/** + * Set the current touchscreen in the configuration. + */ +void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen); + +/** + * Return the current ACONFIGURATION_DENSITY_* set in the configuration. + */ +int32_t AConfiguration_getDensity(AConfiguration* config); + +/** + * Set the current density in the configuration. + */ +void AConfiguration_setDensity(AConfiguration* config, int32_t density); + +/** + * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration. + */ +int32_t AConfiguration_getKeyboard(AConfiguration* config); + +/** + * Set the current keyboard in the configuration. + */ +void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard); + +/** + * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration. + */ +int32_t AConfiguration_getNavigation(AConfiguration* config); + +/** + * Set the current navigation in the configuration. + */ +void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation); + +/** + * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration. + */ +int32_t AConfiguration_getKeysHidden(AConfiguration* config); + +/** + * Set the current keys hidden in the configuration. + */ +void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden); + +/** + * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration. + */ +int32_t AConfiguration_getNavHidden(AConfiguration* config); + +/** + * Set the current nav hidden in the configuration. + */ +void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden); + +/** + * Return the current SDK (API) version set in the configuration. + */ +int32_t AConfiguration_getSdkVersion(AConfiguration* config); + +/** + * Set the current SDK version in the configuration. + */ +void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion); + +/** + * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration. + */ +int32_t AConfiguration_getScreenSize(AConfiguration* config); + +/** + * Set the current screen size in the configuration. + */ +void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize); + +/** + * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration. + */ +int32_t AConfiguration_getScreenLong(AConfiguration* config); + +/** + * Set the current screen long in the configuration. + */ +void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong); + +/** + * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration. + */ +int32_t AConfiguration_getUiModeType(AConfiguration* config); + +/** + * Set the current UI mode type in the configuration. + */ +void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType); + +/** + * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration. + */ +int32_t AConfiguration_getUiModeNight(AConfiguration* config); + +/** + * Set the current UI mode night in the configuration. + */ +void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight); + +/** + * Perform a diff between two configurations. Returns a bit mask of + * ACONFIGURATION_* constants, each bit set meaning that configuration element + * is different between them. + */ +int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2); + +/** + * Determine whether 'base' is a valid configuration for use within the + * environment 'requested'. Returns 0 if there are any values in 'base' + * that conflict with 'requested'. Returns 1 if it does not conflict. + */ +int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested); + +/** + * Determine whether the configuration in 'test' is better than the existing + * configuration in 'base'. If 'requested' is non-NULL, this decision is based + * on the overall configuration given there. If it is NULL, this decision is + * simply based on which configuration is more specific. Returns non-0 if + * 'test' is better than 'base'. + * + * This assumes you have already filtered the configurations with + * AConfiguration_match(). + */ +int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test, + AConfiguration* requested); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_CONFIGURATION_H diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/input.h b/ndk/platforms/android-9/arch-arm/usr/include/android/input.h index 0b8c7e49d..243c33c3a 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/input.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/input.h @@ -40,6 +40,7 @@ * NOTE: These functions MUST be implemented by /system/lib/libui.so */ +#include #include #include #include @@ -268,7 +269,6 @@ enum { /* * Input sources. * - * The appropriate interpretation for an input event depends on its source. * Refer to the documentation on android.view.InputDevice for more details about input sources * and their correct interpretation. */ @@ -296,6 +296,37 @@ enum { AINPUT_SOURCE_JOYSTICK_RIGHT = 0x02000000 | AINPUT_SOURCE_CLASS_JOYSTICK, }; +/* + * Keyboard types. + * + * Refer to the documentation on android.view.InputDevice for more details. + */ +enum { + AINPUT_KEYBOARD_TYPE_NONE = 0, + AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1, + AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2, +}; + +/* + * Constants used to retrieve information about the range of motion for a particular + * coordinate of a motion event. + * + * Refer to the documentation on android.view.InputDevice for more details about input sources + * and their correct interpretation. + */ +enum { + AINPUT_MOTION_RANGE_X = 0, + AINPUT_MOTION_RANGE_Y = 1, + AINPUT_MOTION_RANGE_PRESSURE = 2, + AINPUT_MOTION_RANGE_SIZE = 3, + AINPUT_MOTION_RANGE_TOUCH_MAJOR = 4, + AINPUT_MOTION_RANGE_TOUCH_MINOR = 5, + AINPUT_MOTION_RANGE_TOOL_MAJOR = 6, + AINPUT_MOTION_RANGE_TOOL_MINOR = 7, + AINPUT_MOTION_RANGE_ORIENTATION = 8, +}; + + /* * Input event accessors. * @@ -475,7 +506,7 @@ float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_ * upwards, is perfectly circular or is of unknown orientation. A positive angle * indicates that the major axis of contact is oriented to the right. A negative angle * indicates that the major axis of contact is oriented to the left. - * The full range is from -PI/4 radians (finger pointing fully left) to PI/4 radians + * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians * (finger pointing fully right). */ float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index); @@ -575,7 +606,7 @@ float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_ * upwards, is perfectly circular or is of unknown orientation. A positive angle * indicates that the major axis of contact is oriented to the right. A negative angle * indicates that the major axis of contact is oriented to the left. - * The full range is from -PI/4 radians (finger pointing fully left) to PI/4 radians + * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians * (finger pointing fully right). */ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); @@ -631,6 +662,64 @@ int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event); */ void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); +/* + * Input devices. + * + * These functions provide a mechanism for querying the set of available input devices + * and their characteristics and capabilities. + */ +struct AInputDevice; +typedef struct AInputDevice AInputDevice; + +/* + * Populates the supplied array with the ids of all input devices in the system. + * Sets nActual to the actual number of devices. + * Returns zero if enumeration was successful. + * Returns non-zero if the actual number of devices is greater than nMax, in which case the + * client should call the method again with a larger id buffer. + */ +int32_t AInputDevice_getDeviceIds(int32_t* idBuf, size_t nMax, size_t* nActual); + +/* + * Acquires a device by id. + * Returns NULL if the device was not found. + * + * Note: The returned object must be freed using AInputDevice_release when no longer needed. + */ +AInputDevice* AInputDevice_acquire(int32_t deviceId); + +/* + * Releases a device previously acquired by AInputDevice_acquire. + * If device is NULL, this function does nothing. + */ +void AInputDevice_release(AInputDevice* device); + +/* + * Gets the name of an input device. + * + * Note: The caller should copy the name into a private buffer since the returned pointer + * will become invalid when the device object is released. + */ +const char* AInputDevice_getName(AInputDevice* device); + +/* + * Gets the combination of input sources provided by the input device. + */ +uint32_t AInputDevice_getSources(AInputDevice* device); + +/* + * Gets the keyboard type. + */ +int32_t AInputDevice_getKeyboardType(AInputDevice* device); + +/* Gets the minimum value, maximum value, flat position and error tolerance for a + * particular motion coodinate. + * Returns zero if the device supports the specified motion range. */ +int32_t AInputDevice_getMotionRange(AInputDevice* device, int32_t rangeType, + float* outMin, float* outMax, float* outFlat, float* outFuzz); + +//TODO hasKey, keymap stuff, etc... + #ifdef __cplusplus } #endif diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/native_activity.h b/ndk/platforms/android-9/arch-arm/usr/include/android/native_activity.h index ee4204d88..d74e1ce5a 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/native_activity.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/native_activity.h @@ -196,6 +196,12 @@ typedef struct ANativeActivityCallbacks { */ void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect); + /** + * The current device AConfiguration has changed. The new configuration can + * be retrieved from assetManager. + */ + void (*onConfigurationChanged)(ANativeActivity* activity); + /** * The system is running low on memory. Use this callback to release * resources you do not need, to help the system avoid killing more @@ -208,7 +214,9 @@ typedef struct ANativeActivityCallbacks { * This is the function that must be in the native code to instantiate the * application's native activity. It is called with the activity instance (see * above); if the code is being instantiated from a previously saved instance, - * the savedState will be non-NULL and point to the saved data. + * the savedState will be non-NULL and point to the saved data. You must make + * any copy of this data you need -- it will be released after you return from + * this function. */ typedef void ANativeActivity_createFunc(ANativeActivity* activity, void* savedState, size_t savedStateSize); diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/native_window.h b/ndk/platforms/android-9/arch-arm/usr/include/android/native_window.h index 7599d7e55..ad03d0e93 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/native_window.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/native_window.h @@ -36,12 +36,23 @@ struct ANativeWindow; typedef struct ANativeWindow ANativeWindow; typedef struct ANativeWindow_Buffer { + // The number of pixels that are show horizontally. int32_t width; + + // The number of pixels that are shown vertically. int32_t height; + + // The number of *pixels* that a line in the buffer takes in + // memory. This may be >= width. int32_t stride; + + // The format of the buffer. One of WINDOW_FORMAT_* int32_t format; + + // The actual bits. void* bits; + // Do not touch. uint32_t reserved[6]; } ANativeWindow_Buffer; diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/sensor.h b/ndk/platforms/android-9/arch-arm/usr/include/android/sensor.h index 4291d3e88..b4ce02426 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/sensor.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/sensor.h @@ -87,6 +87,7 @@ enum { * A sensor event. */ +/* NOTE: Must match hardware/sensors.h */ typedef struct ASensorVector { union { float v[3]; @@ -95,23 +96,33 @@ typedef struct ASensorVector { float y; float z; }; + struct { + float azimuth; + float pitch; + float roll; + }; }; int8_t status; uint8_t reserved[3]; } ASensorVector; +/* NOTE: Must match hardware/sensors.h */ typedef struct ASensorEvent { - int sensor; + int32_t version; /* sizeof(struct ASensorEvent) */ + int32_t sensor; + int32_t type; int32_t reserved0; + int64_t timestamp; union { float data[16]; + ASensorVector vector; ASensorVector acceleration; ASensorVector magnetic; float temperature; float distance; float light; + float pressure; }; - int64_t timestamp; int32_t reserved1[4]; } ASensorEvent; @@ -124,6 +135,8 @@ typedef struct ASensorEventQueue ASensorEventQueue; struct ASensor; typedef struct ASensor ASensor; +typedef ASensor const* ASensorRef; +typedef ASensorRef const* ASensorList; /*****************************************************************************/ @@ -141,13 +154,13 @@ ASensorManager* ASensorManager_getInstance(); /* * Returns the list of available sensors. */ -int ASensorManager_getSensorList(ASensorManager* manager, ASensor** list); +int ASensorManager_getSensorList(ASensorManager* manager, ASensorList* list); /* * Returns the default sensor for the given type, or NULL if no sensor * of that type exist. */ -ASensor* ASensorManager_getDefaultSensor(ASensorManager* manager, int type); +ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type); /* * Creates a new sensor event queue and associate it with a looper. @@ -166,20 +179,21 @@ int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* /* * Enable the selected sensor. Returns a negative error code on failure. */ -int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor* sensor); +int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor); /* * Disable the selected sensor. Returns a negative error code on failure. */ -int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor* sensor); +int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor); /* * Sets the delivery rate of events in microseconds for the given sensor. * Note that this is a hint only, generally event will arrive at a higher - * rate. + * rate. It is an error to set a rate inferior to the value returned by + * ASensor_getMinDelay(). * Returns a negative error code on failure. */ -int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor* sensor, int32_t usec); +int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec); /* * Returns true if there are one or more events available in the @@ -210,22 +224,29 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, /* * Returns this sensor's name (non localized) */ -const char* ASensor_getName(ASensor* sensor); +const char* ASensor_getName(ASensor const* sensor); /* * Returns this sensor's vendor's name (non localized) */ -const char* ASensor_getVendor(ASensor* sensor); +const char* ASensor_getVendor(ASensor const* sensor); /* * Return this sensor's type */ -int ASensor_getType(ASensor* sensor); +int ASensor_getType(ASensor const* sensor); /* * Returns this sensors's resolution */ -float ASensor_getResolution(ASensor* sensor); +float ASensor_getResolution(ASensor const* sensor); + +/* + * Returns the minimum delay allowed between events in microseconds. + * A value of zero means that this sensor doesn't report events at a + * constant rate, but rather only when a new data is available. + */ +int ASensor_getMinDelay(ASensor const* sensor); #ifdef __cplusplus diff --git a/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so b/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so index 04a1e58ec1d502feac29cdc0118f4e8575ac87a3..1b41c6758047e4508cf2f760f71387e2a9a6ef32 100644 GIT binary patch literal 26336 zcmeHwdwf$>w*Nj!`k)O`p(-G13Pn+%Hav?@nl^zxfYPFZ!%P~QKmutKl9Wg3{2C~D z3uS)7h$D}xa7P_L@RE^^g98@?o$-M)siQMKXR0GJh?WK)H$|EDe%E<4JEy6S`#1aZ zSzYV9*V=3Ez4jyfoSd9Cm`g2!ATT9q*%+pvNyt1=C2Eo;F#}76E|U!bXN+_Toq$ZF zlSwHLD5fXM7$Zs|l))tbm5^mRtYNGjM1DyRkyS#cP%0b}k)G<9dnRB9>yyOt$c3qQ z(j2r;CNE`xl=i&@o3G{4zXZPkY6qPJT?G9nNJ+CG{1sFLx)O98lnHtUGy(J#$Od`~ z^aSWZkdjV7@XKHb_#b5XCh#4g{h;}vR*;f@D+}krXDXTChh+I7@V7yCfy{E=J2HP2 z{0>+h3CIEZ8R$LGK@g>-~uBo_B~C(z4rIo}RwEq$LP zd>0F)#@?04Ym${GAE8=akuuA5o ziUK?bRL5Zwe+)8$l0bQ&Ac)e{puwUP`%?ySd<=@xR{Xt%%bUo_Bu?k>CRv^>^KCNE z03QWf3YsV9{Y2(7WNrs{%QDZLGWZqvQ?h&~_y$=fwpx}?$$YoWH-b0FaufIwSteEk zdV#|gkgGYI4f(K)x68a7d^2bZ=uJ=!1p2+4w-5YZpc_CfpnrgV z2igm|2lNJr()*xiLF+*ugVc%E>5ki_&)C73Vf^@!OmlG$^fcE4sInd~ot z-vc~C#v`f2av~cO@N(#B4Uqjp;Df-lFA(;r?6<4%V=DX`;5L+>AeVndrPpZCA51oy zm*jsKu#%`f<5W0bh39d2Bt;d_Cnao7)hCvd7<-xgpqhYtYH;_P1m zp3CX~3A}*AS{=?|95w*EID8lIa$qpAfAb*Ja(F55YG9@R8-O=(_;KK^z+hteV-OzZ z@O!{JIQ$jxqa4n_Cb@^hU;Ovh9U*Pob z0t@Dtzb@dbfR%XblQF&=&H^6I;X8rH11szK$G}RWHtqqwle2#g*bFS~{VD8Km3fHSeb7(V4@5IR^nF;N{%_!#g8&b}RZ2Zwur4{|sU2c{P|yd3x~4(|j0gu@>K|C__t;)C(ZnX&Ph z2t16#Wx!d$*hbOj6t)_8G>3Ns-wd27>;C|JCvchEpML-=iDEKjAg-67r)wBmKVyKE zMEXMDgU~CVcMDYddf-=}$48V{eyd9VI~D#La8i-9Us3yYG*EJ)`qF`~;`Fzx^aa49 zIlWJ%-=)IO0S~Q|u7R^~WfTUsp}ZY+QGd^=>{IAaBqwT50q{GpCo{7DkxIW2_!H<; zW&Kks{oBA7IDLmoe+@S7i=2KuZRB#I`ip^u;+X#`m3}?&08am?O5di!;>`!n|E*Ul zwNd{V`-uwwOoflC@TV#~oSevs#3KB;l55}nD*amEVbIgGr1(Cl(!T^e8v3EK{w?4; zIeZ5A9$*9XG@sIjV1Eau>l5-f0$54pIv;o|^ktAqKUbx9sPHD>4`GifF52$`zPUuY zH$ml}R@uL(!vCYf*;tqxQC^uZCR(U+qV_cbZ{_q)sq~)#?|?oHwp4$*a<~^UV->(p za^?Lh{qKMea{4ylRu2CQ_!uy?h5UDOr+gk+Rr=k)$Dmi@e^{meEAUI4 zzC)!SjEVRA-J6uZ&N*O78>ykkhYI>30KnK(B`{ipNovJ_39ZdQ3@C zuOB8?E@H+es&JkPPX{*7l0F~Ezg4B*1-t-yrT*Wk^ig03r@xvOnw+TrbAgvbuY4Z( zfDZzr$zpqduCjjt_#*VmczmeRXIzW@Z#I>c%8v(D66Lv6xQWAA&_A!j5#YJ-r_`5D zA8c|Wf5U-wl~P6qOJ_F&>wB@Dc^D4K;?_k#Z?e4M_V6&&%YV1Cb z%RaRt;6r_Rg+}uV*_OJhm`^aqx{n@EBWByG9c9ycvt5b2O-97#dCcV{6}eWP9nGD zb9RMwvRI!nH@~XN;rGw-*d1n7D;N7b)y1|IHdVMW2+LGiwKWMcF}i`?8H8b5B|^L3 zV|8Lg+zyss;Hg>cT2kw?1zesQYn8`~F^tbc7-v;gCCaTylv`@6SyGD#Cn!+hsjUh4 z5>+tA=W^5p#5N=-H_uaB<-}ZcIBF7T3mr9nS0Its636OA9-9vfq)(4#+g7-i^j9Cs z`DeQ9_Jq};WWTx!`?7l-K0hLvpvnrd0~M~iM4^$CrJkB42}<7MqNy=&wKq{oNjclK z#2HW*#j;2+a+KS5d~yW1*))n$4_OK-{c4c9KLy2&uSg*9sGty zds2zA6UT2##wl3kah}a(*q@A(%N_oCwnc6Sb_Uuf%<4Mq4!0xFXIj=cRS(z zC5^92Q*K*1KT+m_KAC;Z`r4p*et)eG1EZ=&+HzHy;?6Y7wv-Mx301C|ges4_ze3O? zEEBgws;Zjl^5b~5I$;YXovX^`jt#H%{)xHj=Em$Oo@GWOw~Ut3ndLi*Ea+3D{~G#^ zL#+0`BNEf~J2o*BF$QStl>Tdvjai>2f1iRhI*AHS{#x44{ny9Ml|D5} z!zyi-w3SNRBQ`#9*No*WpPk}X%WwHp?sZgQwiUU|#tC*0?dUkO*H#70Wh=y^ZSQ9T zXP65^`E36!W_hskd)+p2RaOFT>NZK!z8ptGx+>s4g(h>2FREGWF;1f6rnrNO)@D~9 zUvx#lAFsKSxO@e``NRd1+hG&0MPjv1Eq5$7A#i--p|Min(N4Z!y*0RM_!@YVSdgcRgW+AR1P`B_RXBzxHa1Ryb+u=O z!@9^;wQMC$w_GvG@2}isrQBkVk1xh|Fjt--)n2!8;$mDp_?^}j;-talT0e$`6GYv- zdwVbPVm&j*$Fw(EMRtFG?g}QD#Z9Z%Ti>V~C-9EUHO7?t0`&|mA^Q5XnkTgd5Xqd7T=t>;!c}iEG~vH zu1C{)RdjVNPFAS{cARmloTVOIdWsjkaiy>saB5XaTi{zu@@jM0)O@LmGKaM-yi*Z(9-WG>CS*gWAUZvyWVf0*}4Kg@jc4>OO^JIzdx1qU;ri`G7jd(SZhlC& zo~&-q65Kh%K{cVHJSs4!rpjSO1aOnaxz9_rtf>*MwYjjqbOtob9PmB8{cb@@M15#R>;q3a!A653AE*FJHI()VvGfvsw?$|a_Os)XO^S^2~2h%52^>Tm?^!!1tFO6lr{o3Im=t5&PJ zS#d=Y)smnqy=6Q;TvNvdEU%w{VesUpiDeR1q=Zbevnq344y2shyCS51$hjEj`&@S1 znoW4G*QoLg>zNMec8}T%CK&Z7zD~4Vx)to7;qX*D#2avN1*JQO`8D=3k3TLPQ!8qH zi)~d7V+k&S#ap9&vnQfKxL1n>FsU483vu=<VK+aR)wxUn`WV=XJ`OwAX>=b5#5c9FSeMOmJ8 zsb|qrG?wL8;BKSGN4B`yD|EPRs)L%OHySwuidU={?x=X&wc=e2e%Viv3rCPvBAqRuiycRlaWu*` z#q97&W5ZDux1FRmiyB-ga7gmkdxnYI75h4zjEXIGG>JB7tQ?2WSK~?Gk7gU)eJZD0 zJbc9ax)e9*USnJn(8*G6;Hm_!?Jhsv8v-o0d^>8szv~ISabnNPcN5CB z97RNow^FrYfLomiRBYV%C1ac@byc=CPEi}jCWzt=gH-5PY^!w#Vo~ayBx-kRV>*t+ z)hJ%}iVLt5H%{Usu5w1}NGeOFI8c|w4d=IXOTBGPtH3u_xXbCOp_7U#24?eQe<|%% zyuGe1+c!D^dpV&(r&k-CT zj_=P85y$u3hl%6+=-(2@_qIog7vLFX;&{gGMdEAldkW%s(D^iRJS-k2z823a6JLkl z@ep5+XS0bvfbWlqH{tng;u{$|OMEk)WhVYJ{Dy`2gZK>$@vZo+2l0m(yF~n9e0_}X zpRaxd--8q1fp3$E@5J}u#CPF)W#TP(hJg5^cs7UlV|Yf4_~UpMhxl$hgF$=`V`GT_ z63-$Ne-giWA-*5qqZ2=XXa0#F#4`iL58)XK;vqbHM*P=!_J;Uj#!89*2G8yg|1F-= zC;mKs`$YT{V+)DDglBVz{~phH5&r{YPU0`)8EN9L;#m{or}0}E;(ugp74a~BgG2lc z#)8D(#53;1|AJ?7h`){TC;kq`pZL2Nf8y_B{E7b!<4?RD<4^n_7=PktF#g2*>z27!<^h>chn)uF zSx6k?Ig>cXvy?c-vy3>#a~^Sw=X~P35ntka5HI+->J!9^_&Lmz_VH(Q?T??iqW!`4 z)X0CI3Ea%^!-5At(!9F)qrkM{sf` zC+j&`$H|wvVr~AClRx9+k2(2$PJWYMWD{*049=H&M| z`Atqf&B-rv@=;Dc%*h8h`3X*L;pA*+oFy)nMHxp)CP)Vv+XEB6&q8&YC|Ie5`tVY34g;PIL^NnEaiGJL%+nI693wyIu zKMKA2Cg}^4bBl9>DfNxH&ZG-M(knyt6K6X$IbXGD+L^&o={+XZ^)gCE|HY1bTYI7} zcO+T#hMwrF9c+}&l2lnx$;9@&jP`iN_GoVz^7h})Y9cyKa;h*=*Um<2+BG9tyLMzx z^!W~Itq$e)cjzh^YN0-i|0I&1IZJ1`!t&F?G3doJ9b;ENbmS%{dRX+lYjxq48H0)k zmDClkDHJRQXSi$Ca)af!Gxk;rhB3?2W=Umsc4WbRzq74ti_vJ=KI7Qg$l7N{UCQoL-ZeB7~<$3~WUG)~<{b_t)Fa zTf43l^%-oTYmvFwMYhx7=Q{Xlacv^}CG&1Gjc7<~5WQBGG;A}8xs@$uF{do|SZ-^9 zGyO=hb7yE>Q*+Z!Q&Jc|_lrCe#E&5(2ZJY@UhB=Y zFRWRUzGkOMi*_sxv$0FV1t?#P^2KrG7mDS7#FeiQ%a?~uYf9wu#VB7K*1Ax?OY`iy z`sVt^a~j=~DcbPEDR+b)1!YF`sC_X0PC^^>gC>SG_38Ce?PF1TY}iy^0xX0_K0Ef= z0(dHR-5*-Fsd*FSy%BmZbaK;cqWqtsr$ddo(Vstc+x;migaD?^)h z4b5U|W9AyFd1sJDsI=j9XjVhxxg?BmO8B0JoD>}*I*8HCKOlM-5S}M`kVacC`hL9q zlyk_D*PLwZt>IG}PjCFCDH~DGJU+~_JBy7=J-$18vq|?sswK;U-A#%|LYf9I&m^9&eXs4x3>l$(- z-NnY{h99W^S$%Rut4oS#wJoMA!av7K(?_%^BO=;CDPcBlF4{0R{43L(@Z+YEaB@Ts zOFb-8!VlM9g;|2RIqq)Q+#S}|>+4%E(wX&#j}+9Git@br^+#x?l(-s0jhjMUnTVAT z-nMDnkyD#aZ)(9@cyrT;(9r8CGB>o-oN0EQ3h6LsT1?5|tC~lLT1?x-=w!4{Zw_@0 zKe67N8lKy146!^alhoc5&F)ra5$$0Stl_6ao0=M%MusMa9x_djyc>jm?DWWAg0D86 z>dpO=ocmgE9oBDp-R)Szw}cL19;I(=F;9;i4{oZvE!0>y7I|qWiZ^aUUTg3aVpbB? zG^95SjttUiB7;)UifoK+68LD$qU3Ri@pC5j#p6MilX;wF>mq`VX2v5ytY{5JI-Bfs zwIq=)Gqe+JYzmqhO2}uD>~l1H>c}_w9F3?xXR2+JJj{tO{Tz&|;zbWHdU$cPopjzG zN?&trNV6sl_{VJ}kWHd|A4Z&wogAihKRBXAEm~?hYSGHIXi-Z#Y8em~?uO00a%q`SK9HOMDL zIisx+^C7(%<(f&C`gTckL1=n&ZfLs+F~d4jMl^!Y-zzZR2mNuhWzb8%I6H`K0MTbo zu*o~)1V~et<}Iiz@zU929pqE8%+`2kOb4CV2u(d?P_Ujww2b(PFV=#yW>5pe$|Qc` z3n2q`GM+BuwL$NU7eHyNG`^Bm1wL3FPg}JP@+nak{N5S)pcmG9X9xkvpmp^^L@SKM ziW*k1R>T{b5YZz?FXyo1LL=JVAVdZscMx)gm4pWfLL?VCxpEFWZsKh6VPlH3p&UBX z^hAp;(YdH6+R!cW`~FRzzp0jt)?qjy9R7sP4WzGul+kLyi6Zz3%QB3lQ`Lb_l(P{| zWa4R!9iQ$*oIoc)%!Rmsf}j&1w$wXAYY8rWN91hTf@P8&*)Wizd%@l4F1}v-=}DoY zYXU1GPse#5(@{Q%vIWa3y0S0^C*2j-uUn@1Mmr*VQu8eeQ2Z<9UhXG9 zm3p73t^MRT^^^Z{Kl!PM7CU~zE#whzbZb9-K~vFXWOK##G%nMAqfy08%2V1c#jJ4| z#f-P@8!xg+5p9&R@v(~cE5#{3Pl{1|o)n+>yqRKO7n3x|+y5 zD?DnjbVpBijN zvgBEGSt%pCqkrkVvZ%hWlFDoIEW$(G(KkA;2x)gtsGNnrCUf!|KWAesnjI{=JG!@1 zX!k_Yb;91B=nJ^cNNMcmk)#QoqbunuZECAk)Elm3+oEIqx(Atvf_|jTnP<;uyG5Pj|GYb1%+ZDQJC4u~gG+xxX|A4~X8` z@nP^?3ZijYD98UNB2O#mj*=veU8R`UC|}c`5_w81%POcA_HUoT#_T;VY`+4wDXl_+ zJWZcGKh6O|EG#=Ii*1`=*-*U~*ZcKiU9<+fqdA>)YDbiErEBC!>m!)OndogE#+Jrw zq$N9)CysD-=WUi;%N3!ga3!9$PZ-r5HFPFhGC~yN%=WZhV=L2lF0It>*jIVQ_Oq3P zc8#@_h6Y~H=?5QdkxwA&!6#wdyQ67{B{`@A%#nH)Qf%ypd%cN?VG#bl{(WP{j={nT^{S!^WEfZBsy$+>I`PJ--U zmyS8n$FI+`F}ioVqnFML&Y7kG?Z+-A8-!biic!(DNhfBCIo29{&H1vcxGMB*a(T575pbt$GX1$7jy#L&C06m9#HU4Hzb?~k)$#?nJL2HO(+g3Ykf z6>u8pHxmvYDmCPn&Y4qYF1MDIo6GXc&DQ+V(mD58XU~~!Er4jsFDR*CNGTy8pc5QiOcYJX6~`-6w&d>W(6 z>2d@vN8oYKuMtOaFPaIa-H=4&@Um={g|imEH3d@ znV*#TA7%cY%s-a-d6}od50x{>e2mN|gVXy1?*zx=zv9c|t_9PBh7+%p-mgeldA%96 zkrKU^k=m{#dZvrsw>Y#?dJmqG&+|)&!)5yJ81OOE0i=S6&zOA=TjFus4aS^l`9@@j zFU(@XO%Rjv%V!x@Ol8SwDfy|w08N@!FAUaY3Rel&Xom{JHP>ocj?W`L6rbaF;`i_Y z+ai{OLOFPI6p8lLHMVM370bczQk)|6udZe}HJ*SYXGu+M4plsUh5XW=9Q?kEO27_} z*^9{MkqQNTl3V;@*5_J;cV_un4xX<^?I_}P2N*oz4_?uPzf%2po}cB=Zx*|-8;zu+dL~D)mY26X+#zLfZOo?o0j$=BDd1OcH zk%-o#l4w0qY5)xYksYmjBH9NC6Olg}zfzE5NBaR0?FVYRM&yxiT3gCm*{n1UdE}20 z9+&U6qdko%JXlnTe-u0NMKl34AMckV+9z5_e-u6KOC*bJo`ZToxtyKi?;**KUcVjp z|3?r;F)97s4@7&5*z`&WAlgTir1-<*+~WJ*$&U6LkrX3vY7?bCG6mP?h#zsj>6KJj|Z6;k>mGu8e)OP<10@G73 literal 17940 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 UK9j8je#ZIG>zv$=$5EF51p{Ib3jhEB diff --git a/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml b/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml index e0e6bf08a..1fd3620bc 100644 --- a/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml +++ b/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml @@ -1,13 +1,22 @@ + + package="com.example.native_activity" + android:versionCode="1" + android:versionName="1.0"> + + - + + + + + + android:label="@string/app_name" + android:configChanges="orientation|keyboardHidden"> + @@ -16,4 +25,6 @@ + + diff --git a/ndk/platforms/android-9/samples/native-activity/jni/main.c b/ndk/platforms/android-9/samples/native-activity/jni/main.c index 4807fd95b..9d83f041f 100644 --- a/ndk/platforms/android-9/samples/native-activity/jni/main.c +++ b/ndk/platforms/android-9/samples/native-activity/jni/main.c @@ -15,6 +15,7 @@ * */ +//BEGIN_INCLUDE(all) #include #include @@ -23,6 +24,18 @@ #include "glutils.h" +/** + * Our saved state data. + */ +struct saved_state { + float angle; + int32_t x; + int32_t y; +}; + +/** + * Shared state for our app. + */ struct engine { struct android_app* app; @@ -32,11 +45,12 @@ struct engine { EGLContext context; int32_t width; int32_t height; - float angle; - int32_t x; - int32_t y; + struct saved_state state; }; +/** + * Initialize an EGL context for the current display. + */ static int engine_init_display(struct engine* engine) { // initialize opengl and egl const EGLint attribs[] = { @@ -68,7 +82,7 @@ static int engine_init_display(struct engine* engine) { engine->surface = surface; engine->width = w; engine->height = h; - engine->angle = 0; + engine->state.angle = 0; // Initialize GL state. glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); @@ -79,39 +93,26 @@ static int engine_init_display(struct engine* engine) { return 0; } +/** + * Just the current frame in the display. + */ static void engine_draw_frame(struct engine* engine) { if (engine->display == NULL) { // No display. return; } - glClearColor(((float)engine->x)/engine->width, engine->angle, - ((float)engine->y)/engine->height, 1); + // Just fill the screen with a color. + glClearColor(((float)engine->state.x)/engine->width, engine->state.angle, + ((float)engine->state.y)/engine->height, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -#if 0 - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(0, 0, -3.0f); - glRotatef(engine->angle, 0, 1, 0); - glRotatef(engine->angle*0.25f, 1, 0, 0); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - //mCube.draw(gl); - - glRotatef(engine->angle*2.0f, 0, 1, 1); - glTranslatef(0.5f, 0.5f, 0.5f); - - //mCube.draw(gl); -#endif - eglSwapBuffers(engine->display, engine->surface); - - //engine->angle += 1.2f; } +/** + * Tear down the EGL context currently associated with the display. + */ static int engine_term_display(struct engine* engine) { if (engine->display != EGL_NO_DISPLAY) { eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -129,89 +130,106 @@ static int engine_term_display(struct engine* engine) { engine->surface = EGL_NO_SURFACE; } -static int engine_do_ui_event(struct engine* engine) { - AInputEvent* event = NULL; - if (AInputQueue_getEvent(engine->app->inputQueue, &event) >= 0) { - LOGI("New input event: type=%d\n", AInputEvent_getType(event)); - if (AInputQueue_preDispatchEvent(engine->app->inputQueue, event)) { - return 1; - } - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - engine->animating = 1; - engine->x = AMotionEvent_getX(event, 0); - engine->y = AMotionEvent_getY(event, 0); - AInputQueue_finishEvent(engine->app->inputQueue, event, 1); - } else { - AInputQueue_finishEvent(engine->app->inputQueue, event, 0); - } - } else { - LOGI("Failure reading next input event: %s\n", strerror(errno)); +/** + * 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 (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { + engine->animating = 1; + engine->state.x = AMotionEvent_getX(event, 0); + engine->state.y = AMotionEvent_getY(event, 0); + return 1; } - - return 1; + return 0; } -static int32_t engine_do_main_cmd(struct engine* engine) { - int32_t res; - int8_t cmd = android_app_read_cmd(engine->app); +/** + * Process the next main command. + */ +static void engine_handle_cmd(struct android_app* app, int32_t cmd) { + struct engine* engine = (struct engine*)app->userData; switch (cmd) { - case APP_CMD_WINDOW_CHANGED: - engine_term_display(engine); - res = android_app_exec_cmd(engine->app, cmd); + case APP_CMD_SAVE_STATE: + 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_INIT_WINDOW: if (engine->app->window != NULL) { engine_init_display(engine); engine_draw_frame(engine); } break; + case APP_CMD_TERM_WINDOW: + engine_term_display(engine); + break; case APP_CMD_LOST_FOCUS: - res = android_app_exec_cmd(engine->app, cmd); engine->animating = 0; engine_draw_frame(engine); break; - default: - res = android_app_exec_cmd(engine->app, cmd); - break; } - - return res; } +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ void android_main(struct android_app* state) { struct engine engine; + // Make sure glue isn't stripped. + app_dummy(); + memset(&engine, 0, sizeof(engine)); state->userData = &engine; + state->onAppCmd = engine_handle_cmd; + state->onInputEvent = engine_handle_input; engine.app = state; + if (state->savedState != NULL) { + // We are starting with a previous saved state; restore from it. + engine.state = *(struct saved_state*)state->savedState; + } + // loop waiting for stuff to do. while (1) { // Read all pending events. int fd; int events; - void* data; - while ((fd=ALooper_pollAll(engine.animating ? 0 : -1, &events, &data)) >= 0) { - switch ((int)data) { - case LOOPER_ID_MAIN: - if (!engine_do_main_cmd(&engine)) { - LOGI("Engine thread destroy requested!"); - engine_term_display(&engine); - return; - } - break; - case LOOPER_ID_EVENT: - engine_do_ui_event(&engine); - break; + 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. + while ((fd=ALooper_pollAll(engine.animating ? 0 : -1, &events, + (void**)&source)) >= 0) { + + // Process this event. + if (source != NULL) { + source->process(state); + } + + // Check if we are exiting. + if (state->destroyRequested != 0) { + engine_term_display(&engine); + return; } } if (engine.animating) { // Done with events; draw next animation frame. - engine.angle += .01f; - if (engine.angle > 1) { - engine.angle = 0; + engine.state.angle += .01f; + if (engine.state.angle > 1) { + engine.state.angle = 0; } + + // Drawing is throttled to the screen update rate, so there + // is no need to do timing here. engine_draw_frame(&engine); } } } +//END_INCLUDE(all) diff --git a/ndk/platforms/android-9/samples/native-activity/src/Dummy.java b/ndk/platforms/android-9/samples/native-activity/src/Dummy.java new file mode 100644 index 000000000..596215a48 --- /dev/null +++ b/ndk/platforms/android-9/samples/native-activity/src/Dummy.java @@ -0,0 +1,4 @@ +// Temporary until the NDK build system can deal with there being no Java source. +class Dummy { + +} diff --git a/ndk/platforms/android-9/samples/native-plasma/jni/plasma.c b/ndk/platforms/android-9/samples/native-plasma/jni/plasma.c index 6cf8b8e2c..6d725ae89 100644 --- a/ndk/platforms/android-9/samples/native-plasma/jni/plasma.c +++ b/ndk/platforms/android-9/samples/native-plasma/jni/plasma.c @@ -411,53 +411,37 @@ static int engine_term_display(struct engine* engine) { engine->animating = 0; } -static int engine_do_ui_event(struct engine* engine) { - AInputEvent* event = NULL; - if (AInputQueue_getEvent(engine->app->inputQueue, &event) >= 0) { - if (AInputQueue_preDispatchEvent(engine->app->inputQueue, event)) { - return 1; - } - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - engine->animating = 1; - AInputQueue_finishEvent(engine->app->inputQueue, event, 1); - } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { - LOGI("Key event: action=%d keyCode=%d metaState=0x%x", - AKeyEvent_getAction(event), - AKeyEvent_getKeyCode(event), - AKeyEvent_getMetaState(event)); - AInputQueue_finishEvent(engine->app->inputQueue, event, 0); - } else { - AInputQueue_finishEvent(engine->app->inputQueue, event, 0); - } - } else { - LOGI("Failure reading next input event: %s\n", strerror(errno)); +static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { + struct engine* engine = (struct engine*)app->userData; + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { + engine->animating = 1; + return 1; + } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { + LOGI("Key event: action=%d keyCode=%d metaState=0x%x", + AKeyEvent_getAction(event), + AKeyEvent_getKeyCode(event), + AKeyEvent_getMetaState(event)); } - return 1; + return 0; } -static int32_t engine_do_main_cmd(struct engine* engine) { - int32_t res; - int8_t cmd = android_app_read_cmd(engine->app); +static void engine_handle_cmd(struct android_app* app, int32_t cmd) { + struct engine* engine = (struct engine*)app->userData; switch (cmd) { - case APP_CMD_WINDOW_CHANGED: - engine_term_display(engine); - res = android_app_exec_cmd(engine->app, cmd); + case APP_CMD_INIT_WINDOW: if (engine->app->window != NULL) { engine_draw_frame(engine); } break; + case APP_CMD_TERM_WINDOW: + engine_term_display(engine); + break; case APP_CMD_LOST_FOCUS: - res = android_app_exec_cmd(engine->app, cmd); engine->animating = 0; engine_draw_frame(engine); break; - default: - res = android_app_exec_cmd(engine->app, cmd); - break; } - - return res; } void android_main(struct android_app* state) { @@ -465,8 +449,13 @@ void android_main(struct android_app* state) { struct engine engine; + // Make sure glue isn't stripped. + app_dummy(); + memset(&engine, 0, sizeof(engine)); state->userData = &engine; + state->onAppCmd = engine_handle_cmd; + state->onInputEvent = engine_handle_input; engine.app = state; if (!init) { @@ -482,19 +471,24 @@ void android_main(struct android_app* state) { // Read all pending events. int fd; int events; - void* data; - while ((fd=ALooper_pollAll(engine.animating ? 0 : -1, &events, &data)) >= 0) { - switch ((int)data) { - case LOOPER_ID_MAIN: - if (!engine_do_main_cmd(&engine)) { - LOGI("Engine thread destroy requested!"); - engine_term_display(&engine); - return; - } - break; - case LOOPER_ID_EVENT: - engine_do_ui_event(&engine); - break; + 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. + while ((fd=ALooper_pollAll(engine.animating ? 0 : -1, &events, + (void**)&source)) >= 0) { + + // Process this event. + if (source != NULL) { + source->process(state); + } + + // Check if we are exiting. + if (state->destroyRequested != 0) { + LOGI("Engine thread destroy requested!"); + engine_term_display(&engine); + return; } } diff --git a/ndk/platforms/android-9/samples/native-plasma/src/Dummy.java b/ndk/platforms/android-9/samples/native-plasma/src/Dummy.java index 4160c66b9..596215a48 100644 --- a/ndk/platforms/android-9/samples/native-plasma/src/Dummy.java +++ b/ndk/platforms/android-9/samples/native-plasma/src/Dummy.java @@ -1,4 +1,4 @@ +// Temporary until the NDK build system can deal with there being no Java source. +class Dummy { -// Only needed for the build system. -public class Dummy { } diff --git a/ndk/sources/android/native_app_glue/android_native_app_glue.c b/ndk/sources/android/native_app_glue/android_native_app_glue.c index d7a839171..05c638ede 100644 --- a/ndk/sources/android/native_app_glue/android_native_app_glue.c +++ b/ndk/sources/android/native_app_glue/android_native_app_glue.c @@ -26,19 +26,58 @@ #include #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "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 { - LOGW("No data on command pipe!"); + LOGI("No data on command pipe!"); } return -1; } -int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { +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"); @@ -50,22 +89,30 @@ int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { if (android_app->inputQueue != NULL) { LOGI("Attaching input queue to looper"); AInputQueue_attachLooper(android_app->inputQueue, - android_app->looper, NULL, (void*)LOOPER_ID_EVENT); + android_app->looper, NULL, &android_app->inputPollSource); } pthread_cond_broadcast(&android_app->cond); pthread_mutex_unlock(&android_app->mutex); break; - case APP_CMD_WINDOW_CHANGED: - LOGI("APP_CMD_WINDOW_CHANGED\n"); + 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_START: + 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_RESUME: + case APP_CMD_START: case APP_CMD_PAUSE: case APP_CMD_STOP: LOGI("activityState=%d\n", cmd); @@ -75,32 +122,101 @@ int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { 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() { - return android_app->destroyRequested ? 0 : 1; } 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) { + 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) { + 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, POLLIN, NULL, (void*)LOOPER_ID_MAIN); + ALooper_addFd(looper, android_app->msgread, POLLIN, NULL, &android_app->cmdPollSource); android_app->looper = looper; pthread_mutex_lock(&android_app->mutex); @@ -118,7 +234,8 @@ static void* android_app_entry(void* param) { // Native activity interaction (called from main thread) // -------------------------------------------------------------------- -static struct android_app* android_app_create(ANativeActivity* activity) { +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; @@ -126,6 +243,12 @@ static struct android_app* android_app_create(ANativeActivity* 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)); @@ -166,8 +289,13 @@ static void android_app_set_input(struct android_app* android_app, AInputQueue* 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; - android_app_write_cmd(android_app, APP_CMD_WINDOW_CHANGED); + 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); } @@ -214,8 +342,27 @@ static void onResume(ANativeActivity* activity) { } 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); - return NULL; + 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) { @@ -228,8 +375,16 @@ static void onStop(ANativeActivity* 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) { @@ -267,6 +422,7 @@ void ANativeActivity_onCreate(ANativeActivity* activity, 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; @@ -274,5 +430,5 @@ void ANativeActivity_onCreate(ANativeActivity* activity, activity->callbacks->onInputQueueCreated = onInputQueueCreated; activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; - activity->instance = android_app_create(activity); + activity->instance = android_app_create(activity, savedState, savedStateSize); } diff --git a/ndk/sources/android/native_app_glue/android_native_app_glue.h b/ndk/sources/android/native_app_glue/android_native_app_glue.h index e30e9603e..e60f1c079 100644 --- a/ndk/sources/android/native_app_glue/android_native_app_glue.h +++ b/ndk/sources/android/native_app_glue/android_native_app_glue.h @@ -22,8 +22,9 @@ #include #include -#include +#include #include +#include #ifdef __cplusplus extern "C" @@ -59,7 +60,7 @@ extern "C" * - input events coming from the AInputQueue attached to the activity. * * Each of these correspond to an ALooper callback that returns a "data" - * value of LOOPER_ID_MAIN and LOOPER_ID_EVENT, respectively. + * value of LOOPER_ID_MAIN and LOOPER_ID_INPUT, respectively. * * Your application can use the same ALooper to listen to additionnal * file-descriptors. @@ -71,7 +72,7 @@ extern "C" * * XXX: MAKE THIS STUFF MORE CLEAR !! * - * 5/ Whenever you receive a LOOPER_ID_EVENT event from the ALooper, you + * 5/ Whenever you receive a LOOPER_ID_INPUT event from the ALooper, you * should read one event from the AInputQueue with AInputQueue_getEvent(). * * See the sample named "native-activity" that comes with the NDK with a @@ -79,6 +80,25 @@ extern "C" * */ +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 fd 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); +}; + /** * This is the interface for the standard glue code of a threaded * application. In this model, the application's code is running @@ -92,9 +112,32 @@ struct android_app { // 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; @@ -113,6 +156,10 @@ struct android_app { // 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. @@ -124,11 +171,11 @@ struct android_app { pthread_t thread; - // This is non-zero when the application's NativeActivity is being - // destroyed and waiting for the app thread to complete. - int destroyRequested; + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; int running; + int stateSaved; int destroyed; int redrawNeeded; AInputQueue* pendingInputQueue; @@ -149,7 +196,7 @@ enum { * application's window. These can be read via the inputQueue * object of android_app. */ - LOOPER_ID_EVENT = 2 + LOOPER_ID_INPUT = 2 }; enum { @@ -161,11 +208,19 @@ enum { APP_CMD_INPUT_CHANGED, /** - * Command from main thread: the ANativeWindow has changed. Upon processing - * this command, android_app->window will be updated to the new window surface - * (or NULL). + * 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_WINDOW_CHANGED, + 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. @@ -199,6 +254,11 @@ enum { */ 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. @@ -215,6 +275,15 @@ enum { */ 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. */ @@ -240,12 +309,22 @@ int8_t android_app_read_cmd(struct android_app* android_app); /** * Call with the command returned by android_app_read_cmd() to do the - * default processing of the given command. - * - * Important: returns 0 if the app should exit. You must ALWAYS check for - * a zero return and, if found, exit your android_main() function. + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. */ -int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd); +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