Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //--------------------------------------------------------------------------------
     18 // Include files
     19 //--------------------------------------------------------------------------------
     20 #include <jni.h>
     21 #include <errno.h>
     22 
     23 #include <android/sensor.h>
     24 #include <android/log.h>
     25 #include <android_native_app_glue.h>
     26 #include <android/native_window_jni.h>
     27 #include <cpu-features.h>
     28 
     29 #include "TeapotRenderer.h"
     30 #include "NDKHelper.h"
     31 
     32 //-------------------------------------------------------------------------
     33 //Preprocessor
     34 //-------------------------------------------------------------------------
     35 #define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper function
     36 //-------------------------------------------------------------------------
     37 //Shared state for our app.
     38 //-------------------------------------------------------------------------
     39 struct android_app;
     40 class Engine
     41 {
     42     TeapotRenderer renderer_;
     43 
     44     ndk_helper::GLContext* gl_context_;
     45 
     46     bool initialized_resources_;
     47     bool has_focus_;
     48 
     49     ndk_helper::DoubletapDetector doubletap_detector_;
     50     ndk_helper::PinchDetector pinch_detector_;
     51     ndk_helper::DragDetector drag_detector_;
     52     ndk_helper::PerfMonitor monitor_;
     53 
     54     ndk_helper::TapCamera tap_camera_;
     55 
     56     android_app* app_;
     57 
     58     ASensorManager* sensor_manager_;
     59     const ASensor* accelerometer_sensor_;
     60     ASensorEventQueue* sensor_event_queue_;
     61 
     62     void UpdateFPS( float fFPS );
     63     void ShowUI();
     64     void TransformPosition( ndk_helper::Vec2& vec );
     65 
     66 public:
     67     static void HandleCmd( struct android_app* app,
     68             int32_t cmd );
     69     static int32_t HandleInput( android_app* app,
     70             AInputEvent* event );
     71 
     72     Engine();
     73     ~Engine();
     74     void SetState( android_app* state );
     75     int InitDisplay();
     76     void LoadResources();
     77     void UnloadResources();
     78     void DrawFrame();
     79     void TermDisplay();
     80     void TrimMemory();
     81     bool IsReady();
     82 
     83     void UpdatePosition( AInputEvent* event,
     84             int32_t iIndex,
     85             float& fX,
     86             float& fY );
     87 
     88     void InitSensors();
     89     void ProcessSensors( int32_t id );
     90     void SuspendSensors();
     91     void ResumeSensors();
     92 };
     93 
     94 //-------------------------------------------------------------------------
     95 //Ctor
     96 //-------------------------------------------------------------------------
     97 Engine::Engine() :
     98                 initialized_resources_( false ),
     99                 has_focus_( false ),
    100                 app_( NULL ),
    101                 sensor_manager_( NULL ),
    102                 accelerometer_sensor_( NULL ),
    103                 sensor_event_queue_( NULL )
    104 {
    105     gl_context_ = ndk_helper::GLContext::GetInstance();
    106 }
    107 
    108 //-------------------------------------------------------------------------
    109 //Dtor
    110 //-------------------------------------------------------------------------
    111 Engine::~Engine()
    112 {
    113 }
    114 
    115 /**
    116  * Load resources
    117  */
    118 void Engine::LoadResources()
    119 {
    120     renderer_.Init();
    121     renderer_.Bind( &tap_camera_ );
    122 }
    123 
    124 /**
    125  * Unload resources
    126  */
    127 void Engine::UnloadResources()
    128 {
    129     renderer_.Unload();
    130 }
    131 
    132 /**
    133  * Initialize an EGL context for the current display.
    134  */
    135 int Engine::InitDisplay()
    136 {
    137     if( !initialized_resources_ )
    138     {
    139         gl_context_->Init( app_->window );
    140         LoadResources();
    141         initialized_resources_ = true;
    142     }
    143     else
    144     {
    145         // initialize OpenGL ES and EGL
    146         if( EGL_SUCCESS != gl_context_->Resume( app_->window ) )
    147         {
    148             UnloadResources();
    149             LoadResources();
    150         }
    151     }
    152 
    153     ShowUI();
    154 
    155     // Initialize GL state.
    156     glEnable( GL_CULL_FACE );
    157     glEnable( GL_DEPTH_TEST );
    158     glDepthFunc( GL_LEQUAL );
    159 
    160     //Note that screen size might have been changed
    161     glViewport( 0, 0, gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() );
    162     renderer_.UpdateViewport();
    163 
    164     tap_camera_.SetFlip( 1.f, -1.f, -1.f );
    165     tap_camera_.SetPinchTransformFactor( 2.f, 2.f, 8.f );
    166 
    167     return 0;
    168 }
    169 
    170 /**
    171  * Just the current frame in the display.
    172  */
    173 void Engine::DrawFrame()
    174 {
    175     float fFPS;
    176     if( monitor_.Update( fFPS ) )
    177     {
    178         UpdateFPS( fFPS );
    179     }
    180     renderer_.Update( monitor_.GetCurrentTime() );
    181 
    182     // Just fill the screen with a color.
    183     glClearColor( 0.5f, 0.5f, 0.5f, 1.f );
    184     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    185     renderer_.Render();
    186 
    187     // Swap
    188     if( EGL_SUCCESS != gl_context_->Swap() )
    189     {
    190         UnloadResources();
    191         LoadResources();
    192     }
    193 }
    194 
    195 /**
    196  * Tear down the EGL context currently associated with the display.
    197  */
    198 void Engine::TermDisplay()
    199 {
    200     gl_context_->Suspend();
    201 
    202 }
    203 
    204 void Engine::TrimMemory()
    205 {
    206     LOGI( "Trimming memory" );
    207     gl_context_->Invalidate();
    208 }
    209 /**
    210  * Process the next input event.
    211  */
    212 int32_t Engine::HandleInput( android_app* app,
    213         AInputEvent* event )
    214 {
    215     Engine* eng = (Engine*) app->userData;
    216     if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
    217     {
    218         ndk_helper::GESTURE_STATE doubleTapState = eng->doubletap_detector_.Detect( event );
    219         ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event );
    220         ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( event );
    221 
    222         //Double tap detector has a priority over other detectors
    223         if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION )
    224         {
    225             //Detect double tap
    226             eng->tap_camera_.Reset( true );
    227         }
    228         else
    229         {
    230             //Handle drag state
    231             if( dragState & ndk_helper::GESTURE_STATE_START )
    232             {
    233                 //Otherwise, start dragging
    234                 ndk_helper::Vec2 v;
    235                 eng->drag_detector_.GetPointer( v );
    236                 eng->TransformPosition( v );
    237                 eng->tap_camera_.BeginDrag( v );
    238             }
    239             else if( dragState & ndk_helper::GESTURE_STATE_MOVE )
    240             {
    241                 ndk_helper::Vec2 v;
    242                 eng->drag_detector_.GetPointer( v );
    243                 eng->TransformPosition( v );
    244                 eng->tap_camera_.Drag( v );
    245             }
    246             else if( dragState & ndk_helper::GESTURE_STATE_END )
    247             {
    248                 eng->tap_camera_.EndDrag();
    249             }
    250 
    251             //Handle pinch state
    252             if( pinchState & ndk_helper::GESTURE_STATE_START )
    253             {
    254                 //Start new pinch
    255                 ndk_helper::Vec2 v1;
    256                 ndk_helper::Vec2 v2;
    257                 eng->pinch_detector_.GetPointers( v1, v2 );
    258                 eng->TransformPosition( v1 );
    259                 eng->TransformPosition( v2 );
    260                 eng->tap_camera_.BeginPinch( v1, v2 );
    261             }
    262             else if( pinchState & ndk_helper::GESTURE_STATE_MOVE )
    263             {
    264                 //Multi touch
    265                 //Start new pinch
    266                 ndk_helper::Vec2 v1;
    267                 ndk_helper::Vec2 v2;
    268                 eng->pinch_detector_.GetPointers( v1, v2 );
    269                 eng->TransformPosition( v1 );
    270                 eng->TransformPosition( v2 );
    271                 eng->tap_camera_.Pinch( v1, v2 );
    272             }
    273         }
    274         return 1;
    275     }
    276     return 0;
    277 }
    278 
    279 /**
    280  * Process the next main command.
    281  */
    282 void Engine::HandleCmd( struct android_app* app,
    283         int32_t cmd )
    284 {
    285     Engine* eng = (Engine*) app->userData;
    286     switch( cmd )
    287     {
    288     case APP_CMD_SAVE_STATE:
    289         break;
    290     case APP_CMD_INIT_WINDOW:
    291         // The window is being shown, get it ready.
    292         if( app->window != NULL )
    293         {
    294             eng->InitDisplay();
    295             eng->DrawFrame();
    296         }
    297         break;
    298     case APP_CMD_TERM_WINDOW:
    299         // The window is being hidden or closed, clean it up.
    300         eng->TermDisplay();
    301         eng->has_focus_ = false;
    302         break;
    303     case APP_CMD_STOP:
    304         break;
    305     case APP_CMD_GAINED_FOCUS:
    306         eng->ResumeSensors();
    307         //Start animation
    308         eng->has_focus_ = true;
    309         break;
    310     case APP_CMD_LOST_FOCUS:
    311         eng->SuspendSensors();
    312         // Also stop animating.
    313         eng->has_focus_ = false;
    314         eng->DrawFrame();
    315         break;
    316     case APP_CMD_LOW_MEMORY:
    317         //Free up GL resources
    318         eng->TrimMemory();
    319         break;
    320     }
    321 }
    322 
    323 //-------------------------------------------------------------------------
    324 //Sensor handlers
    325 //-------------------------------------------------------------------------
    326 void Engine::InitSensors()
    327 {
    328     sensor_manager_ = ASensorManager_getInstance();
    329     accelerometer_sensor_ = ASensorManager_getDefaultSensor( sensor_manager_,
    330             ASENSOR_TYPE_ACCELEROMETER );
    331     sensor_event_queue_ = ASensorManager_createEventQueue( sensor_manager_, app_->looper,
    332             LOOPER_ID_USER, NULL, NULL );
    333 }
    334 
    335 void Engine::ProcessSensors( int32_t id )
    336 {
    337     // If a sensor has data, process it now.
    338     if( id == LOOPER_ID_USER )
    339     {
    340         if( accelerometer_sensor_ != NULL )
    341         {
    342             ASensorEvent event;
    343             while( ASensorEventQueue_getEvents( sensor_event_queue_, &event, 1 ) > 0 )
    344             {
    345             }
    346         }
    347     }
    348 }
    349 
    350 void Engine::ResumeSensors()
    351 {
    352     // When our app gains focus, we start monitoring the accelerometer.
    353     if( accelerometer_sensor_ != NULL )
    354     {
    355         ASensorEventQueue_enableSensor( sensor_event_queue_, accelerometer_sensor_ );
    356         // We'd like to get 60 events per second (in us).
    357         ASensorEventQueue_setEventRate( sensor_event_queue_, accelerometer_sensor_,
    358                 (1000L / 60) * 1000 );
    359     }
    360 }
    361 
    362 void Engine::SuspendSensors()
    363 {
    364     // When our app loses focus, we stop monitoring the accelerometer.
    365     // This is to avoid consuming battery while not being used.
    366     if( accelerometer_sensor_ != NULL )
    367     {
    368         ASensorEventQueue_disableSensor( sensor_event_queue_, accelerometer_sensor_ );
    369     }
    370 }
    371 
    372 //-------------------------------------------------------------------------
    373 //Misc
    374 //-------------------------------------------------------------------------
    375 void Engine::SetState( android_app* state )
    376 {
    377     app_ = state;
    378     doubletap_detector_.SetConfiguration( app_->config );
    379     drag_detector_.SetConfiguration( app_->config );
    380     pinch_detector_.SetConfiguration( app_->config );
    381 }
    382 
    383 bool Engine::IsReady()
    384 {
    385     if( has_focus_ )
    386         return true;
    387 
    388     return false;
    389 }
    390 
    391 void Engine::TransformPosition( ndk_helper::Vec2& vec )
    392 {
    393     vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec
    394             / ndk_helper::Vec2( gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() )
    395             - ndk_helper::Vec2( 1.f, 1.f );
    396 }
    397 
    398 void Engine::ShowUI()
    399 {
    400     JNIEnv *jni;
    401     app_->activity->vm->AttachCurrentThread( &jni, NULL );
    402 
    403     //Default class retrieval
    404     jclass clazz = jni->GetObjectClass( app_->activity->clazz );
    405     jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" );
    406     jni->CallVoidMethod( app_->activity->clazz, methodID );
    407 
    408     app_->activity->vm->DetachCurrentThread();
    409     return;
    410 }
    411 
    412 void Engine::UpdateFPS( float fFPS )
    413 {
    414     JNIEnv *jni;
    415     app_->activity->vm->AttachCurrentThread( &jni, NULL );
    416 
    417     //Default class retrieval
    418     jclass clazz = jni->GetObjectClass( app_->activity->clazz );
    419     jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" );
    420     jni->CallVoidMethod( app_->activity->clazz, methodID, fFPS );
    421 
    422     app_->activity->vm->DetachCurrentThread();
    423     return;
    424 }
    425 
    426 Engine g_engine;
    427 
    428 /**
    429  * This is the main entry point of a native application that is using
    430  * android_native_app_glue.  It runs in its own thread, with its own
    431  * event loop for receiving input events and doing other things.
    432  */
    433 void android_main( android_app* state )
    434 {
    435     app_dummy();
    436 
    437     g_engine.SetState( state );
    438 
    439     //Init helper functions
    440     ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME );
    441 
    442     state->userData = &g_engine;
    443     state->onAppCmd = Engine::HandleCmd;
    444     state->onInputEvent = Engine::HandleInput;
    445 
    446 #ifdef USE_NDK_PROFILER
    447     monstartup("libTeapotNativeActivity.so");
    448 #endif
    449 
    450     // Prepare to monitor accelerometer
    451     g_engine.InitSensors();
    452 
    453     // loop waiting for stuff to do.
    454     while( 1 )
    455     {
    456         // Read all pending events.
    457         int id;
    458         int events;
    459         android_poll_source* source;
    460 
    461         // If not animating, we will block forever waiting for events.
    462         // If animating, we loop until all events are read, then continue
    463         // to draw the next frame of animation.
    464         while( (id = ALooper_pollAll( g_engine.IsReady() ? 0 : -1, NULL, &events, (void**) &source ))
    465                 >= 0 )
    466         {
    467             // Process this event.
    468             if( source != NULL )
    469                 source->process( state, source );
    470 
    471             g_engine.ProcessSensors( id );
    472 
    473             // Check if we are exiting.
    474             if( state->destroyRequested != 0 )
    475             {
    476                 g_engine.TermDisplay();
    477                 return;
    478             }
    479         }
    480 
    481         if( g_engine.IsReady() )
    482         {
    483             // Drawing is throttled to the screen update rate, so there
    484             // is no need to do timing here.
    485             g_engine.DrawFrame();
    486         }
    487     }
    488 }
    489