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 04a1e58ec..1b41c6758 100644 Binary files a/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so and b/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so differ 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