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
This commit is contained in:
@@ -18,8 +18,6 @@
|
||||
#ifndef ANDROID_ASSET_MANAGER_H
|
||||
#define ANDROID_ASSET_MANAGER_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#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,
|
||||
|
||||
@@ -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 <android/asset_manager.h>
|
||||
#include <jni.h>
|
||||
|
||||
#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
|
||||
@@ -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 <android/asset_manager.h>
|
||||
|
||||
#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
|
||||
@@ -40,6 +40,7 @@
|
||||
* NOTE: These functions MUST be implemented by /system/lib/libui.so
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <android/keycodes.h>
|
||||
#include <android/looper.h>
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
@@ -1,13 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- BEGIN_INCLUDE(manifest) -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.native_activity"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<!-- This is the platform API where NativeActivity was introduced. -->
|
||||
<uses-sdk android:minSdkVersion="8" />
|
||||
<application android:label="@string/app_name"
|
||||
android:hasCode="false" android:debuggable="true">
|
||||
|
||||
<!-- This .apk has no Java code itself, so set hasCode to false. -->
|
||||
<application android:label="@string/app_name" android:hasCode="false">
|
||||
|
||||
<!-- Our activity is the built-in NativeActivity framework class.
|
||||
This will take care of integrating with our NDK code. -->
|
||||
<activity android:name="android.app.NativeActivity"
|
||||
android:label="@string/app_name">
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<!-- Tell NativeActivity the name of or .so -->
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="native-activity" />
|
||||
<intent-filter>
|
||||
@@ -16,4 +25,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
<!-- END_INCLUDE(manifest) -->
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
//BEGIN_INCLUDE(all)
|
||||
#include <jni.h>
|
||||
|
||||
#include <errno.h>
|
||||
@@ -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;
|
||||
}
|
||||
/**
|
||||
* 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->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));
|
||||
}
|
||||
|
||||
engine->state.x = AMotionEvent_getX(event, 0);
|
||||
engine->state.y = AMotionEvent_getY(event, 0);
|
||||
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!");
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case LOOPER_ID_EVENT:
|
||||
engine_do_ui_event(&engine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// Temporary until the NDK build system can deal with there being no Java source.
|
||||
class Dummy {
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
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;
|
||||
AInputQueue_finishEvent(engine->app->inputQueue, event, 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));
|
||||
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));
|
||||
}
|
||||
|
||||
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,20 +471,25 @@ 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)) {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case LOOPER_ID_EVENT:
|
||||
engine_do_ui_event(&engine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (engine.animating) {
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
@@ -26,19 +26,58 @@
|
||||
#include <android/log.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <android/native_activity.h>
|
||||
#include <android/configuration.h>
|
||||
#include <android/looper.h>
|
||||
#include <android/native_activity.h>
|
||||
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user