src/core/android/SDL_android.cpp
author Sam Lantinga <slouken@libsdl.org>
Sun, 23 Sep 2012 01:37:44 -0700
changeset 6448 64a6297a8b93
parent 6392 fa7eb111f994
child 6464 ab55284b389f
permissions -rwxr-xr-x
Fixed bug 1606 - SDL does not implement SDL_GetPowerInfo() for Android.

Philipp Wiesemann 2012-09-22 05:26:11 PDT

currently SDL (HG) does not implement the power management functionality of
SDL_GetPowerInfo() for Android.

I attached a patch which tries to implement this functionality (JNI only, API
5). It supports plugged state and battery percent return values but not
remaining seconds (which are not available on Android).
slouken@4964
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6138
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
slouken@4964
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@4964
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@4964
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@4964
    20
*/
slouken@4964
    21
#include "SDL_config.h"
slouken@5222
    22
#include "SDL_stdinc.h"
slouken@6284
    23
#include "SDL_assert.h"
slouken@4964
    24
slouken@6044
    25
#ifdef __ANDROID__
slouken@6044
    26
slouken@4989
    27
#include "SDL_android.h"
slouken@4989
    28
slouken@4980
    29
extern "C" {
slouken@5092
    30
#include "../../events/SDL_events_c.h"
slouken@5092
    31
#include "../../video/android/SDL_androidkeyboard.h"
slouken@5092
    32
#include "../../video/android/SDL_androidtouch.h"
slouken@5092
    33
#include "../../video/android/SDL_androidvideo.h"
slouken@4995
    34
icculus@5996
    35
#include <android/log.h>
gabomdq@6354
    36
#include <pthread.h>
icculus@5996
    37
#define LOG_TAG "SDL_android"
icculus@5996
    38
//#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
icculus@5996
    39
//#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
icculus@5996
    40
#define LOGI(...) do {} while (false)
icculus@5996
    41
#define LOGE(...) do {} while (false)
icculus@5996
    42
icculus@5996
    43
slouken@6191
    44
/* Implemented in audio/android/SDL_androidaudio.c */
slouken@4995
    45
extern void Android_RunAudioThread();
slouken@4995
    46
} // C
slouken@4980
    47
slouken@4964
    48
/*******************************************************************************
slouken@4964
    49
 This file links the Java side of Android with libsdl
slouken@4964
    50
*******************************************************************************/
slouken@4964
    51
#include <jni.h>
slouken@4964
    52
#include <android/log.h>
slouken@4964
    53
slouken@4964
    54
slouken@4964
    55
/*******************************************************************************
slouken@4964
    56
                               Globals
slouken@4964
    57
*******************************************************************************/
gabomdq@6354
    58
static pthread_key_t mThreadKey;
icculus@5996
    59
static JavaVM* mJavaVM;
slouken@4964
    60
slouken@4995
    61
// Main activity
slouken@4998
    62
static jclass mActivityClass;
slouken@4964
    63
slouken@4995
    64
// method signatures
slouken@4995
    65
static jmethodID midCreateGLContext;
slouken@4995
    66
static jmethodID midFlipBuffers;
slouken@4995
    67
static jmethodID midAudioInit;
slouken@4995
    68
static jmethodID midAudioWriteShortBuffer;
slouken@4995
    69
static jmethodID midAudioWriteByteBuffer;
slouken@4996
    70
static jmethodID midAudioQuit;
slouken@4964
    71
slouken@4995
    72
// Accelerometer data storage
slouken@5000
    73
static float fLastAccelerometer[3];
slouken@6212
    74
static bool bHasNewData;
slouken@4964
    75
slouken@4964
    76
/*******************************************************************************
slouken@4964
    77
                 Functions called by JNI
slouken@4964
    78
*******************************************************************************/
slouken@4964
    79
slouken@4964
    80
// Library init
slouken@4964
    81
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
slouken@4964
    82
{
icculus@5996
    83
    JNIEnv *env;
icculus@5996
    84
    mJavaVM = vm;
icculus@5996
    85
    LOGI("JNI_OnLoad called");
icculus@5996
    86
    if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
icculus@5996
    87
        LOGE("Failed to get the environment using GetEnv()");
icculus@5996
    88
        return -1;
icculus@5996
    89
    }
gabomdq@6354
    90
    /*
gabomdq@6354
    91
     * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
gabomdq@6354
    92
     * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
gabomdq@6354
    93
     */
gabomdq@6354
    94
    if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed)) {
gabomdq@6354
    95
        __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
gabomdq@6354
    96
    }
gabomdq@6354
    97
    else {
gabomdq@6354
    98
        Android_JNI_SetupThread();
gabomdq@6354
    99
    }
icculus@5996
   100
slouken@4964
   101
    return JNI_VERSION_1_4;
slouken@4964
   102
}
slouken@4964
   103
slouken@4964
   104
// Called before SDL_main() to initialize JNI bindings
gabomdq@6354
   105
extern "C" void SDL_Android_Init(JNIEnv* mEnv, jclass cls)
slouken@4964
   106
{
slouken@4964
   107
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
slouken@4964
   108
gabomdq@6354
   109
    Android_JNI_SetupThread();
gabomdq@6354
   110
gabomdq@6354
   111
    mActivityClass = (jclass)mEnv->NewGlobalRef(cls);
slouken@4998
   112
slouken@4998
   113
    midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
slouken@5222
   114
                                "createGLContext","(II)Z");
slouken@4998
   115
    midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
slouken@4998
   116
                                "flipBuffers","()V");
slouken@4998
   117
    midAudioInit = mEnv->GetStaticMethodID(mActivityClass, 
slouken@4998
   118
                                "audioInit", "(IZZI)Ljava/lang/Object;");
slouken@4998
   119
    midAudioWriteShortBuffer = mEnv->GetStaticMethodID(mActivityClass,
slouken@4998
   120
                                "audioWriteShortBuffer", "([S)V");
slouken@4998
   121
    midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
slouken@4998
   122
                                "audioWriteByteBuffer", "([B)V");
slouken@4998
   123
    midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
slouken@4998
   124
                                "audioQuit", "()V");
slouken@4964
   125
slouken@6212
   126
    bHasNewData = false;
slouken@6212
   127
slouken@4995
   128
    if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
slouken@4996
   129
       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
slouken@4996
   130
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
slouken@4964
   131
    }
icculus@5996
   132
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
slouken@4964
   133
}
slouken@4964
   134
slouken@4995
   135
// Resize
slouken@4995
   136
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
slouken@4998
   137
                                    JNIEnv* env, jclass jcls,
slouken@4995
   138
                                    jint width, jint height, jint format)
slouken@4995
   139
{
slouken@4995
   140
    Android_SetScreenResolution(width, height, format);
slouken@4995
   141
}
slouken@4995
   142
slouken@4964
   143
// Keydown
slouken@4995
   144
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
slouken@4998
   145
                                    JNIEnv* env, jclass jcls, jint keycode)
slouken@4964
   146
{
slouken@4980
   147
    Android_OnKeyDown(keycode);
slouken@4964
   148
}
slouken@4964
   149
slouken@4964
   150
// Keyup
slouken@4995
   151
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
slouken@4998
   152
                                    JNIEnv* env, jclass jcls, jint keycode)
slouken@4964
   153
{
slouken@4980
   154
    Android_OnKeyUp(keycode);
slouken@4964
   155
}
slouken@4964
   156
slouken@4964
   157
// Touch
slouken@4995
   158
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(
slouken@4998
   159
                                    JNIEnv* env, jclass jcls,
icculus@5982
   160
                                    jint touch_device_id_in, jint pointer_finger_id_in,
slouken@4995
   161
                                    jint action, jfloat x, jfloat y, jfloat p)
slouken@4964
   162
{
icculus@5982
   163
    Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
slouken@4964
   164
}
slouken@4964
   165
slouken@4995
   166
// Accelerometer
slouken@4964
   167
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
slouken@4998
   168
                                    JNIEnv* env, jclass jcls,
slouken@4995
   169
                                    jfloat x, jfloat y, jfloat z)
slouken@4964
   170
{
slouken@4964
   171
    fLastAccelerometer[0] = x;
slouken@4964
   172
    fLastAccelerometer[1] = y;
slouken@6212
   173
    fLastAccelerometer[2] = z;
slouken@6212
   174
    bHasNewData = true;
slouken@4964
   175
}
slouken@4964
   176
slouken@4995
   177
// Quit
slouken@4995
   178
extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit(
slouken@4998
   179
                                    JNIEnv* env, jclass cls)
slouken@4995
   180
{    
slouken@4995
   181
    // Inject a SDL_QUIT event
slouken@4995
   182
    SDL_SendQuit();
slouken@4995
   183
}
slouken@4995
   184
slouken@6186
   185
// Pause
slouken@6186
   186
extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
slouken@6186
   187
                                    JNIEnv* env, jclass cls)
slouken@6186
   188
{
slouken@6186
   189
    if (Android_Window) {
gabomdq@6330
   190
        /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself */
gabomdq@6330
   191
        if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
slouken@6186
   192
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
slouken@6191
   193
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
slouken@6186
   194
    }
slouken@6186
   195
}
slouken@6186
   196
slouken@6186
   197
// Resume
slouken@6186
   198
extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
slouken@6186
   199
                                    JNIEnv* env, jclass cls)
slouken@6186
   200
{
slouken@6186
   201
    if (Android_Window) {
gabomdq@6330
   202
        /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
gabomdq@6330
   203
         * We can't restore the GL Context here because it needs to be done on the SDL main thread
gabomdq@6330
   204
         * and this function will be called from the Java thread instead.
gabomdq@6330
   205
         */
gabomdq@6330
   206
        if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
slouken@6186
   207
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
slouken@6191
   208
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
slouken@6186
   209
    }
slouken@6186
   210
}
slouken@6186
   211
slouken@4995
   212
extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
slouken@4998
   213
                                    JNIEnv* env, jclass cls)
slouken@4995
   214
{
slouken@4998
   215
    /* This is the audio thread, with a different environment */
gabomdq@6354
   216
    Android_JNI_SetupThread();
slouken@4998
   217
slouken@4996
   218
    Android_RunAudioThread();
slouken@4995
   219
}
slouken@4964
   220
slouken@4964
   221
slouken@4964
   222
/*******************************************************************************
slouken@4964
   223
             Functions called by SDL into Java
slouken@4964
   224
*******************************************************************************/
slouken@6284
   225
slouken@6284
   226
class LocalReferenceHolder
slouken@6284
   227
{
slouken@6284
   228
private:
slouken@6284
   229
    static int s_active;
slouken@6284
   230
slouken@6284
   231
public:
slouken@6284
   232
    static bool IsActive() {
slouken@6284
   233
        return s_active > 0;
slouken@6284
   234
    }
slouken@6284
   235
slouken@6284
   236
public:
slouken@6284
   237
    LocalReferenceHolder() : m_env(NULL) { }
slouken@6284
   238
    ~LocalReferenceHolder() {
slouken@6284
   239
        if (m_env) {
slouken@6284
   240
            m_env->PopLocalFrame(NULL);
slouken@6284
   241
            --s_active;
slouken@6284
   242
        }
slouken@6284
   243
    }
slouken@6284
   244
slouken@6284
   245
    bool init(JNIEnv *env, jint capacity = 16) {
slouken@6284
   246
        if (env->PushLocalFrame(capacity) < 0) {
slouken@6284
   247
            SDL_SetError("Failed to allocate enough JVM local references");
slouken@6284
   248
            return false;
slouken@6284
   249
        }
slouken@6284
   250
        ++s_active;
slouken@6284
   251
        m_env = env;
slouken@6284
   252
        return true;
slouken@6284
   253
    }
slouken@6284
   254
slouken@6284
   255
protected:
slouken@6284
   256
    JNIEnv *m_env;
slouken@6284
   257
};
slouken@6284
   258
int LocalReferenceHolder::s_active;
slouken@6284
   259
slouken@5222
   260
extern "C" SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion)
slouken@4964
   261
{
gabomdq@6354
   262
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@5222
   263
    if (mEnv->CallStaticBooleanMethod(mActivityClass, midCreateGLContext, majorVersion, minorVersion)) {
slouken@5222
   264
        return SDL_TRUE;
slouken@5222
   265
    } else {
slouken@5222
   266
        return SDL_FALSE;
slouken@5222
   267
    }
slouken@4964
   268
}
slouken@4964
   269
slouken@4989
   270
extern "C" void Android_JNI_SwapWindow()
slouken@4964
   271
{
gabomdq@6354
   272
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@4998
   273
    mEnv->CallStaticVoidMethod(mActivityClass, midFlipBuffers); 
slouken@4998
   274
}
slouken@4998
   275
slouken@4998
   276
extern "C" void Android_JNI_SetActivityTitle(const char *title)
slouken@4998
   277
{
slouken@4998
   278
    jmethodID mid;
gabomdq@6354
   279
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@4998
   280
    mid = mEnv->GetStaticMethodID(mActivityClass,"setActivityTitle","(Ljava/lang/String;)V");
slouken@4998
   281
    if (mid) {
gabomdq@6307
   282
        jstring jtitle = reinterpret_cast<jstring>(mEnv->NewStringUTF(title));
gabomdq@6307
   283
        mEnv->CallStaticVoidMethod(mActivityClass, mid, jtitle);
gabomdq@6307
   284
        mEnv->DeleteLocalRef(jtitle);
slouken@4998
   285
    }
slouken@4964
   286
}
slouken@4964
   287
slouken@6212
   288
extern "C" SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
slouken@5000
   289
{
slouken@5000
   290
    int i;
slouken@6212
   291
    SDL_bool retval = SDL_FALSE;
slouken@6212
   292
slouken@6212
   293
    if (bHasNewData) {
slouken@6212
   294
        for (i = 0; i < 3; ++i) {
slouken@6212
   295
            values[i] = fLastAccelerometer[i];
slouken@6212
   296
        }
slouken@6212
   297
        bHasNewData = false;
slouken@6212
   298
        retval = SDL_TRUE;
slouken@5000
   299
    }
slouken@6212
   300
slouken@6212
   301
    return retval;
slouken@5000
   302
}
slouken@5000
   303
gabomdq@6354
   304
static void Android_JNI_ThreadDestroyed(void* value) {
gabomdq@6354
   305
    /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
gabomdq@6354
   306
    JNIEnv *env = (JNIEnv*) value;
gabomdq@6354
   307
    if (env != NULL) {
gabomdq@6354
   308
        mJavaVM->DetachCurrentThread();
gabomdq@6354
   309
        pthread_setspecific(mThreadKey, NULL);
gabomdq@6354
   310
    }
gabomdq@6354
   311
}
gabomdq@6354
   312
gabomdq@6354
   313
JNIEnv* Android_JNI_GetEnv(void) {
gabomdq@6354
   314
    /* From http://developer.android.com/guide/practices/jni.html
gabomdq@6354
   315
     * All threads are Linux threads, scheduled by the kernel.
gabomdq@6354
   316
     * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
gabomdq@6354
   317
     * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
gabomdq@6354
   318
     * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
gabomdq@6354
   319
     * and cannot make JNI calls.
gabomdq@6354
   320
     * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
gabomdq@6354
   321
     * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
gabomdq@6354
   322
     * is a no-op.
gabomdq@6354
   323
     * Note: You can call this function any number of times for the same thread, there's no harm in it
gabomdq@6354
   324
     */
gabomdq@6354
   325
gabomdq@6354
   326
    JNIEnv *env;
gabomdq@6354
   327
    int status = mJavaVM->AttachCurrentThread(&env, NULL);
gabomdq@6354
   328
    if(status < 0) {
gabomdq@6354
   329
        LOGE("failed to attach current thread");
gabomdq@6354
   330
        return 0;
gabomdq@6354
   331
    }
gabomdq@6354
   332
gabomdq@6354
   333
    return env;
gabomdq@6354
   334
}
gabomdq@6354
   335
gabomdq@6354
   336
int Android_JNI_SetupThread(void) {
gabomdq@6354
   337
    /* From http://developer.android.com/guide/practices/jni.html
gabomdq@6354
   338
     * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
gabomdq@6354
   339
     * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
gabomdq@6354
   340
     * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
gabomdq@6354
   341
     * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
gabomdq@6354
   342
     * Note: The destructor is not called unless the stored value is != NULL
gabomdq@6354
   343
     * Note: You can call this function any number of times for the same thread, there's no harm in it
gabomdq@6354
   344
     *       (except for some lost CPU cycles)
gabomdq@6354
   345
     */
gabomdq@6354
   346
    JNIEnv *env = Android_JNI_GetEnv();
gabomdq@6354
   347
    pthread_setspecific(mThreadKey, (void*) env);
gabomdq@6354
   348
    return 1;
gabomdq@6354
   349
}
gabomdq@6354
   350
slouken@4995
   351
//
slouken@4995
   352
// Audio support
slouken@4995
   353
//
slouken@4996
   354
static jboolean audioBuffer16Bit = JNI_FALSE;
slouken@4996
   355
static jboolean audioBufferStereo = JNI_FALSE;
slouken@4996
   356
static jobject audioBuffer = NULL;
slouken@4996
   357
static void* audioBufferPinned = NULL;
slouken@4995
   358
slouken@4995
   359
extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
slouken@4964
   360
{
slouken@4996
   361
    int audioBufferFrames;
slouken@4964
   362
icculus@5996
   363
    int status;
gabomdq@6354
   364
    JNIEnv *env = Android_JNI_GetEnv();
gabomdq@6354
   365
gabomdq@6354
   366
    if (!env) {
gabomdq@6354
   367
        LOGE("callback_handler: failed to attach current thread");
icculus@5996
   368
    }
gabomdq@6354
   369
    Android_JNI_SetupThread();
icculus@5996
   370
icculus@5996
   371
    
slouken@4996
   372
    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
slouken@4996
   373
    audioBuffer16Bit = is16Bit;
slouken@4996
   374
    audioBufferStereo = channelCount > 1;
slouken@4964
   375
icculus@5996
   376
    audioBuffer = env->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
slouken@4995
   377
slouken@4996
   378
    if (audioBuffer == NULL) {
slouken@4996
   379
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!");
slouken@4996
   380
        return 0;
slouken@4996
   381
    }
icculus@5996
   382
    audioBuffer = env->NewGlobalRef(audioBuffer);
slouken@4995
   383
slouken@4996
   384
    jboolean isCopy = JNI_FALSE;
slouken@4996
   385
    if (audioBuffer16Bit) {
icculus@5996
   386
        audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
icculus@5996
   387
        audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
slouken@4996
   388
    } else {
icculus@5996
   389
        audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
icculus@5996
   390
        audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer);
slouken@4996
   391
    }
slouken@4996
   392
    if (audioBufferStereo) {
slouken@4996
   393
        audioBufferFrames /= 2;
slouken@4996
   394
    }
icculus@5996
   395
 
slouken@4996
   396
    return audioBufferFrames;
slouken@4995
   397
}
slouken@4995
   398
slouken@4996
   399
extern "C" void * Android_JNI_GetAudioBuffer()
slouken@4995
   400
{
slouken@4996
   401
    return audioBufferPinned;
slouken@4995
   402
}
slouken@4995
   403
slouken@4996
   404
extern "C" void Android_JNI_WriteAudioBuffer()
slouken@4995
   405
{
gabomdq@6354
   406
    JNIEnv *mAudioEnv = Android_JNI_GetEnv();
gabomdq@6354
   407
slouken@4996
   408
    if (audioBuffer16Bit) {
slouken@4996
   409
        mAudioEnv->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
slouken@4998
   410
        mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
slouken@4996
   411
    } else {
slouken@4996
   412
        mAudioEnv->ReleaseByteArrayElements((jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
slouken@4998
   413
        mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
slouken@4996
   414
    }
slouken@4995
   415
slouken@4996
   416
    /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
slouken@4995
   417
}
slouken@4995
   418
slouken@4995
   419
extern "C" void Android_JNI_CloseAudioDevice()
slouken@4995
   420
{
icculus@5996
   421
    int status;
gabomdq@6354
   422
    JNIEnv *env = Android_JNI_GetEnv();
icculus@5996
   423
icculus@5996
   424
    env->CallStaticVoidMethod(mActivityClass, midAudioQuit); 
slouken@4964
   425
slouken@4997
   426
    if (audioBuffer) {
icculus@5996
   427
        env->DeleteGlobalRef(audioBuffer);
slouken@4997
   428
        audioBuffer = NULL;
slouken@4997
   429
        audioBufferPinned = NULL;
slouken@4997
   430
    }
slouken@4964
   431
}
slouken@4981
   432
tim@5650
   433
// Test for an exception and call SDL_SetError with its detail if one occurs
tim@5650
   434
static bool Android_JNI_ExceptionOccurred()
tim@5650
   435
{
slouken@6284
   436
    SDL_assert(LocalReferenceHolder::IsActive());
gabomdq@6354
   437
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@6284
   438
tim@5650
   439
    jthrowable exception = mEnv->ExceptionOccurred();
tim@5650
   440
    if (exception != NULL) {
tim@5650
   441
        jmethodID mid;
tim@5650
   442
tim@5650
   443
        // Until this happens most JNI operations have undefined behaviour
tim@5650
   444
        mEnv->ExceptionClear();
tim@5650
   445
tim@5650
   446
        jclass exceptionClass = mEnv->GetObjectClass(exception);
tim@5650
   447
        jclass classClass = mEnv->FindClass("java/lang/Class");
tim@5650
   448
tim@5650
   449
        mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
tim@5650
   450
        jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
tim@5650
   451
        const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0);
tim@5650
   452
tim@5650
   453
        mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;");
icculus@5860
   454
        jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exception, mid);
tim@5650
   455
tim@5650
   456
        if (exceptionMessage != NULL) {
tim@5650
   457
            const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(
tim@5650
   458
                    exceptionMessage, 0);
tim@5650
   459
            SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
tim@5650
   460
            mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8);
tim@5650
   461
        } else {
tim@5650
   462
            SDL_SetError("%s", exceptionNameUTF8);
tim@5650
   463
        }
tim@5650
   464
tim@5650
   465
        mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8);
tim@5650
   466
tim@5650
   467
        return true;
tim@5650
   468
    }
tim@5650
   469
tim@5650
   470
    return false;
tim@5650
   471
}
tim@5650
   472
icculus@5582
   473
static int Android_JNI_FileOpen(SDL_RWops* ctx)
icculus@5582
   474
{
slouken@6284
   475
    LocalReferenceHolder refs;
tim@5650
   476
    int result = 0;
tim@5650
   477
tim@5650
   478
    jmethodID mid;
tim@5650
   479
    jobject context;
tim@5650
   480
    jobject assetManager;
tim@5650
   481
    jobject inputStream;
tim@5650
   482
    jclass channels;
tim@5650
   483
    jobject readableByteChannel;
tim@5650
   484
    jstring fileNameJString;
tim@5650
   485
gabomdq@6354
   486
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@6284
   487
    if (!refs.init(mEnv)) {
tim@5650
   488
        goto failure;
tim@5650
   489
    }
tim@5650
   490
gabomdq@6308
   491
    fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
icculus@5582
   492
icculus@5582
   493
    // context = SDLActivity.getContext();
tim@5650
   494
    mid = mEnv->GetStaticMethodID(mActivityClass,
icculus@5582
   495
            "getContext","()Landroid/content/Context;");
tim@5650
   496
    context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
icculus@5582
   497
icculus@5582
   498
    // assetManager = context.getAssets();
icculus@5582
   499
    mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
tim@5650
   500
            "getAssets", "()Landroid/content/res/AssetManager;");
tim@5650
   501
    assetManager = mEnv->CallObjectMethod(context, mid);
icculus@5582
   502
icculus@5582
   503
    // inputStream = assetManager.open(<filename>);
icculus@5582
   504
    mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
icculus@5582
   505
            "open", "(Ljava/lang/String;)Ljava/io/InputStream;");
tim@5650
   506
    inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
tim@5650
   507
    if (Android_JNI_ExceptionOccurred()) {
tim@5650
   508
        goto failure;
icculus@5582
   509
    }
icculus@5582
   510
tim@5650
   511
    ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
tim@5650
   512
icculus@5582
   513
    // Despite all the visible documentation on [Asset]InputStream claiming
icculus@5582
   514
    // that the .available() method is not guaranteed to return the entire file
icculus@5582
   515
    // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
icculus@5582
   516
    // android/apis/content/ReadAsset.java imply that Android's
icculus@5582
   517
    // AssetInputStream.available() /will/ always return the total file size
icculus@5582
   518
icculus@5582
   519
    // size = inputStream.available();
icculus@5582
   520
    mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
icculus@5582
   521
            "available", "()I");
icculus@5582
   522
    ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid);
tim@5650
   523
    if (Android_JNI_ExceptionOccurred()) {
tim@5650
   524
        goto failure;
icculus@5582
   525
    }
icculus@5582
   526
icculus@5582
   527
    // readableByteChannel = Channels.newChannel(inputStream);
tim@5650
   528
    channels = mEnv->FindClass("java/nio/channels/Channels");
icculus@5582
   529
    mid = mEnv->GetStaticMethodID(channels,
icculus@5582
   530
            "newChannel",
icculus@5582
   531
            "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
tim@5650
   532
    readableByteChannel = mEnv->CallStaticObjectMethod(
icculus@5582
   533
            channels, mid, inputStream);
tim@5650
   534
    if (Android_JNI_ExceptionOccurred()) {
tim@5650
   535
        goto failure;
icculus@5582
   536
    }
icculus@5582
   537
tim@5650
   538
    ctx->hidden.androidio.readableByteChannelRef =
tim@5650
   539
        mEnv->NewGlobalRef(readableByteChannel);
tim@5650
   540
icculus@5582
   541
    // Store .read id for reading purposes
icculus@5582
   542
    mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
icculus@5582
   543
            "read", "(Ljava/nio/ByteBuffer;)I");
icculus@5582
   544
    ctx->hidden.androidio.readMethod = mid;
icculus@5582
   545
icculus@5582
   546
    ctx->hidden.androidio.position = 0;
icculus@5582
   547
tim@5650
   548
    if (false) {
tim@5650
   549
failure:
tim@5650
   550
        result = -1;
tim@5650
   551
tim@5650
   552
        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
tim@5650
   553
tim@5650
   554
        if(ctx->hidden.androidio.inputStreamRef != NULL) {
tim@5650
   555
            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
tim@5650
   556
        }
gabomdq@6308
   557
gabomdq@6308
   558
        if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
gabomdq@6308
   559
            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
gabomdq@6308
   560
        }
gabomdq@6308
   561
tim@5650
   562
    }
tim@5650
   563
tim@5650
   564
    return result;
icculus@5582
   565
}
icculus@5582
   566
icculus@5582
   567
extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
icculus@5582
   568
        const char* fileName, const char*)
icculus@5582
   569
{
slouken@6284
   570
    LocalReferenceHolder refs;
gabomdq@6354
   571
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@6284
   572
slouken@6284
   573
    if (!refs.init(mEnv)) {
slouken@6284
   574
        return -1;
slouken@6284
   575
    }
slouken@6284
   576
icculus@5582
   577
    if (!ctx) {
icculus@5582
   578
        return -1;
icculus@5582
   579
    }
icculus@5582
   580
icculus@5582
   581
    jstring fileNameJString = mEnv->NewStringUTF(fileName);
icculus@5582
   582
    ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
tim@5650
   583
    ctx->hidden.androidio.inputStreamRef = NULL;
gabomdq@6335
   584
    ctx->hidden.androidio.readableByteChannelRef = NULL;
gabomdq@6335
   585
    ctx->hidden.androidio.readMethod = NULL;
icculus@5582
   586
icculus@5582
   587
    return Android_JNI_FileOpen(ctx);
icculus@5582
   588
}
icculus@5582
   589
icculus@5582
   590
extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
icculus@5582
   591
        size_t size, size_t maxnum)
icculus@5582
   592
{
slouken@6284
   593
    LocalReferenceHolder refs;
gabomdq@6377
   594
    jlong bytesRemaining = (jlong) (size * maxnum);
gabomdq@6377
   595
    jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
icculus@5582
   596
    int bytesRead = 0;
icculus@5582
   597
gabomdq@6377
   598
    /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
gabomdq@6377
   599
    if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
gabomdq@6377
   600
gabomdq@6354
   601
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@6284
   602
    if (!refs.init(mEnv)) {
slouken@6284
   603
        return -1;
slouken@6284
   604
    }
slouken@6284
   605
gabomdq@6308
   606
    jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
icculus@5582
   607
    jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
icculus@5582
   608
    jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
icculus@5582
   609
icculus@5582
   610
    while (bytesRemaining > 0) {
icculus@5582
   611
        // result = readableByteChannel.read(...);
icculus@5582
   612
        int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
icculus@5582
   613
tim@5650
   614
        if (Android_JNI_ExceptionOccurred()) {
icculus@5582
   615
            return 0;
icculus@5582
   616
        }
icculus@5582
   617
icculus@5582
   618
        if (result < 0) {
icculus@5582
   619
            break;
icculus@5582
   620
        }
icculus@5582
   621
icculus@5582
   622
        bytesRemaining -= result;
icculus@5582
   623
        bytesRead += result;
icculus@5582
   624
        ctx->hidden.androidio.position += result;
icculus@5582
   625
    }
icculus@5582
   626
icculus@5582
   627
    return bytesRead / size;
icculus@5582
   628
}
icculus@5582
   629
icculus@5582
   630
extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
icculus@5582
   631
        size_t size, size_t num)
icculus@5582
   632
{
icculus@5582
   633
    SDL_SetError("Cannot write to Android package filesystem");
icculus@5582
   634
    return 0;
icculus@5582
   635
}
icculus@5582
   636
icculus@5582
   637
static int Android_JNI_FileClose(SDL_RWops* ctx, bool release)
icculus@5582
   638
{
slouken@6284
   639
    LocalReferenceHolder refs;
icculus@5582
   640
    int result = 0;
gabomdq@6354
   641
    JNIEnv *mEnv = Android_JNI_GetEnv();
icculus@5582
   642
slouken@6284
   643
    if (!refs.init(mEnv)) {
slouken@6284
   644
        SDL_SetError("Failed to allocate enough JVM local references");
slouken@6284
   645
        return -1;
slouken@6284
   646
    }
slouken@6284
   647
icculus@5582
   648
    if (ctx) {
icculus@5582
   649
        if (release) {
icculus@5582
   650
            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
icculus@5582
   651
        }
icculus@5582
   652
gabomdq@6308
   653
        jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
icculus@5582
   654
icculus@5582
   655
        // inputStream.close();
icculus@5582
   656
        jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
icculus@5582
   657
                "close", "()V");
icculus@5582
   658
        mEnv->CallVoidMethod(inputStream, mid);
icculus@5582
   659
        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
icculus@5582
   660
        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
tim@5650
   661
        if (Android_JNI_ExceptionOccurred()) {
icculus@5582
   662
            result = -1;
icculus@5582
   663
        }
icculus@5582
   664
icculus@5582
   665
        if (release) {
icculus@5582
   666
            SDL_FreeRW(ctx);
icculus@5582
   667
        }
icculus@5582
   668
    }
icculus@5582
   669
icculus@5582
   670
    return result;
icculus@5582
   671
}
icculus@5582
   672
icculus@5582
   673
icculus@5582
   674
extern "C" long Android_JNI_FileSeek(SDL_RWops* ctx, long offset, int whence)
icculus@5582
   675
{
icculus@5582
   676
    long newPosition;
icculus@5582
   677
icculus@5582
   678
    switch (whence) {
icculus@5582
   679
        case RW_SEEK_SET:
icculus@5582
   680
            newPosition = offset;
icculus@5582
   681
            break;
icculus@5582
   682
        case RW_SEEK_CUR:
icculus@5582
   683
            newPosition = ctx->hidden.androidio.position + offset;
icculus@5582
   684
            break;
icculus@5582
   685
        case RW_SEEK_END:
icculus@5582
   686
            newPosition = ctx->hidden.androidio.size + offset;
icculus@5582
   687
            break;
icculus@5582
   688
        default:
icculus@5582
   689
            SDL_SetError("Unknown value for 'whence'");
icculus@5582
   690
            return -1;
icculus@5582
   691
    }
icculus@5582
   692
    if (newPosition < 0) {
icculus@5582
   693
        newPosition = 0;
icculus@5582
   694
    }
icculus@5582
   695
    if (newPosition > ctx->hidden.androidio.size) {
icculus@5582
   696
        newPosition = ctx->hidden.androidio.size;
icculus@5582
   697
    }
icculus@5582
   698
icculus@5582
   699
    long movement = newPosition - ctx->hidden.androidio.position;
gabomdq@6308
   700
    jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
icculus@5582
   701
icculus@5582
   702
    if (movement > 0) {
tim@5993
   703
        unsigned char buffer[1024];
tim@5993
   704
icculus@5582
   705
        // The easy case where we're seeking forwards
icculus@5582
   706
        while (movement > 0) {
icculus@5994
   707
            long amount = (long) sizeof (buffer);
icculus@5994
   708
            if (amount > movement) {
icculus@5994
   709
                amount = movement;
icculus@5994
   710
            }
icculus@5994
   711
            size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
tim@5993
   712
tim@5993
   713
            if (result <= 0) {
tim@5993
   714
                // Failed to read/skip the required amount, so fail
icculus@5582
   715
                return -1;
icculus@5582
   716
            }
tim@5993
   717
tim@5993
   718
            movement -= result;
icculus@5582
   719
        }
icculus@5582
   720
    } else if (movement < 0) {
icculus@5582
   721
        // We can't seek backwards so we have to reopen the file and seek
icculus@5582
   722
        // forwards which obviously isn't very efficient
icculus@5582
   723
        Android_JNI_FileClose(ctx, false);
icculus@5582
   724
        Android_JNI_FileOpen(ctx);
icculus@5582
   725
        Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
icculus@5582
   726
    }
icculus@5582
   727
icculus@5582
   728
    ctx->hidden.androidio.position = newPosition;
icculus@5582
   729
icculus@5582
   730
    return ctx->hidden.androidio.position;
icculus@5582
   731
}
icculus@5582
   732
icculus@5582
   733
extern "C" int Android_JNI_FileClose(SDL_RWops* ctx)
icculus@5582
   734
{
icculus@5582
   735
    return Android_JNI_FileClose(ctx, true);
icculus@5582
   736
}
icculus@5582
   737
slouken@6448
   738
// returns 0 on success or -1 on error (others undefined then)
slouken@6448
   739
// returns truthy or falsy value in plugged, charged and battery
slouken@6448
   740
// returns the value in seconds and percent or -1 if not available
slouken@6448
   741
extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
slouken@6448
   742
{
slouken@6448
   743
    LocalReferenceHolder refs;
slouken@6448
   744
    JNIEnv* env = Android_JNI_GetEnv();
slouken@6448
   745
    if (!refs.init(env)) {
slouken@6448
   746
        return -1;
slouken@6448
   747
    }
slouken@6448
   748
slouken@6448
   749
    jmethodID mid;
slouken@6448
   750
slouken@6448
   751
    mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;");
slouken@6448
   752
    jobject context = env->CallStaticObjectMethod(mActivityClass, mid);
slouken@6448
   753
slouken@6448
   754
    jstring action = env->NewStringUTF("android.intent.action.BATTERY_CHANGED");
slouken@6448
   755
slouken@6448
   756
    jclass cls = env->FindClass("android/content/IntentFilter");
slouken@6448
   757
slouken@6448
   758
    mid = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
slouken@6448
   759
    jobject filter = env->NewObject(cls, mid, action);
slouken@6448
   760
slouken@6448
   761
    env->DeleteLocalRef(action);
slouken@6448
   762
slouken@6448
   763
    mid = env->GetMethodID(mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
slouken@6448
   764
    jobject intent = env->CallObjectMethod(context, mid, NULL, filter);
slouken@6448
   765
slouken@6448
   766
    env->DeleteLocalRef(filter);
slouken@6448
   767
slouken@6448
   768
    cls = env->GetObjectClass(intent);
slouken@6448
   769
slouken@6448
   770
    jstring iname;
slouken@6448
   771
    jmethodID imid = env->GetMethodID(cls, "getIntExtra", "(Ljava/lang/String;I)I");
slouken@6448
   772
slouken@6448
   773
#define GET_INT_EXTRA(var, key) \
slouken@6448
   774
    iname = env->NewStringUTF(key); \
slouken@6448
   775
    int var = env->CallIntMethod(intent, imid, iname, -1); \
slouken@6448
   776
    env->DeleteLocalRef(iname);
slouken@6448
   777
slouken@6448
   778
    jstring bname;
slouken@6448
   779
    jmethodID bmid = env->GetMethodID(cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
slouken@6448
   780
slouken@6448
   781
#define GET_BOOL_EXTRA(var, key) \
slouken@6448
   782
    bname = env->NewStringUTF(key); \
slouken@6448
   783
    int var = env->CallBooleanMethod(intent, bmid, bname, JNI_FALSE); \
slouken@6448
   784
    env->DeleteLocalRef(bname);
slouken@6448
   785
slouken@6448
   786
    if (plugged) {
slouken@6448
   787
        GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5)
slouken@6448
   788
        if (plug == -1) {
slouken@6448
   789
            return -1;
slouken@6448
   790
        }
slouken@6448
   791
        // 1 == BatteryManager.BATTERY_PLUGGED_AC
slouken@6448
   792
        // 2 == BatteryManager.BATTERY_PLUGGED_USB
slouken@6448
   793
        *plugged = (0 < plug) ? 1 : 0;
slouken@6448
   794
    }
slouken@6448
   795
slouken@6448
   796
    if (charged) {
slouken@6448
   797
        GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5)
slouken@6448
   798
        if (status == -1) {
slouken@6448
   799
            return -1;
slouken@6448
   800
        }
slouken@6448
   801
        // 5 == BatteryManager.BATTERY_STATUS_FULL
slouken@6448
   802
        *charged = (status == 5) ? 1 : 0;
slouken@6448
   803
    }
slouken@6448
   804
slouken@6448
   805
    if (battery) {
slouken@6448
   806
        GET_BOOL_EXTRA(present, "present") // == BatteryManager.EXTRA_PRESENT (API 5)
slouken@6448
   807
        *battery = present ? 1 : 0;
slouken@6448
   808
    }
slouken@6448
   809
slouken@6448
   810
    if (seconds) {
slouken@6448
   811
        *seconds = -1; // not possible
slouken@6448
   812
    }
slouken@6448
   813
slouken@6448
   814
    if (percent) {
slouken@6448
   815
        GET_INT_EXTRA(level, "level") // == BatteryManager.EXTRA_LEVEL (API 5)
slouken@6448
   816
        GET_INT_EXTRA(scale, "scale") // == BatteryManager.EXTRA_SCALE (API 5)
slouken@6448
   817
        if ((level == -1) || (scale == -1)) {
slouken@6448
   818
            return -1;
slouken@6448
   819
        }
slouken@6448
   820
        *percent = level * 100 / scale;
slouken@6448
   821
    }
slouken@6448
   822
slouken@6448
   823
    env->DeleteLocalRef(intent);
slouken@6448
   824
slouken@6448
   825
    return 0;
slouken@6448
   826
}
slouken@6448
   827
slouken@6392
   828
// sends message to be handled on the UI event dispatch thread
slouken@6392
   829
extern "C" int Android_JNI_SendMessage(int command, int param)
slouken@6392
   830
{
slouken@6392
   831
    JNIEnv *env = Android_JNI_GetEnv();
slouken@6392
   832
    if (!env) {
slouken@6392
   833
        return -1;
slouken@6392
   834
    }
slouken@6392
   835
    jmethodID mid = env->GetStaticMethodID(mActivityClass, "sendMessage", "(II)V");
slouken@6392
   836
    if (!mid) {
slouken@6392
   837
        return -1;
slouken@6392
   838
    }
slouken@6392
   839
    env->CallStaticVoidMethod(mActivityClass, mid, command, param);
slouken@6392
   840
    return 0;
slouken@6392
   841
}
slouken@6392
   842
slouken@6044
   843
#endif /* __ANDROID__ */
slouken@6044
   844
slouken@4981
   845
/* vi: set ts=4 sw=4 expandtab: */