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