Browse Source

Several additions on Android.

App is now in fullscreen mode, the screen won't dim anymore, make JNI call saver, enable orientation sensor, fix permissions, cleanup of old logger messages.
Joachim Meyer 9 năm trước cách đây
mục cha
commit
37d68a9ce9

+ 4 - 10
build/android/TemplateApp/AndroidManifest.xml

@@ -1,17 +1,11 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.native_activity"
-    android:versionCode="1"
-    android:versionName="1.0">
+    package="org.polycode.templateapp">
 
-    <uses-sdk android:minSdkVersion="21" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <application android:label="TemplateApp">
-
-
-        <android:uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-        <android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
-        <android:uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
         <activity
             android:name="org.polycode.templateapp.PolycodeActivity"
             android:configChanges="keyboardHidden"

+ 10 - 14
build/android/TemplateApp/jni/PolycodeTemplateApp.cpp

@@ -15,7 +15,6 @@ PolycodeTemplateApp::PolycodeTemplateApp(PolycodeView *view) {
 	// Write your code here!
     srand(time(NULL));
     scene = new Scene(Scene::SCENE_2D);
-    //scene->getActiveCamera()->setOrthoSize(1920, 1080);
     scene->useClearColor = true;
     scene->clearColor.setColor(1.0f / (float)(rand() % 5),1.0f / (float)(rand()%5),1.0f / (float)(rand() % 5),1.0f);
 
@@ -23,7 +22,6 @@ PolycodeTemplateApp::PolycodeTemplateApp(PolycodeView *view) {
     test->setPositionY(0.2);
     test->setMaterialByName("Unlit");
     test->getShaderPass(0).shaderBinding->loadTextureForParam("diffuse", "main_icon.png");
-    //test->setColor(1.0,0.0,0.0,1.0);
 
     SceneLabel *testLabel = new SceneLabel("Hello Polycode!", 32, "sans", Label::ANTIALIAS_FULL, 0.2);
 	testLabel->setPositionY(-0.2);
@@ -53,6 +51,7 @@ PolycodeTemplateApp::PolycodeTemplateApp(PolycodeView *view) {
 //     sound3 = new Sound("curve_02_c.wav");
 
     //sound2->Play(true);
+
     running = 0;
     rot = Vector3();
     fps = new SceneLabel("0", 32, "sans", Label::ANTIALIAS_FULL, 0.1);
@@ -60,6 +59,15 @@ PolycodeTemplateApp::PolycodeTemplateApp(PolycodeView *view) {
     scene->addChild(fps);
     scene->addChild(test);
 
+    /*game = new Scene(Scene::SCENE_3D);
+    game->useClearColor = true;
+    game->getActiveCamera()->setPosition(0, 0, 10);
+
+    box = new ScenePrimitive(ScenePrimitive::TYPE_BOX, 2, 2, 2);
+    box->setMaterialByName("UnlitUntextured");
+    box->setColor(0.1, 1.0, 1.0, 1.0);
+    game->addChild(box);*/
+
     Services()->getInput()->addEventListener(this, InputEvent::EVENT_KEYDOWN);
     Services()->getInput()->addEventListener(this, InputEvent::EVENT_TOUCHES_BEGAN);
     Services()->getInput()->addEventListener(this, InputEvent::EVENT_TOUCHES_ENDED);
@@ -84,27 +92,19 @@ void PolycodeTemplateApp::handleEvent(Event *event) {
             break;
             }
         } else if (inputEvent->getEventCode() == InputEvent::EVENT_TOUCHES_BEGAN){
-            //Logger::log("Touch began: %f, %f", inputEvent->touch.position.x, inputEvent->touch.position.y);
             Ray r = scene->projectRayFromCameraAndViewportCoordinate(scene->getActiveCamera(),inputEvent->touch.position);
             test->setPosition(r.origin.x, r.origin.y);
         } else if(inputEvent->getEventCode() == InputEvent::EVENT_TOUCHES_ENDED){
-            //Logger::log("Touch ended: %f, %f", inputEvent->touch.position.x, inputEvent->touch.position.y);
             Ray r = scene->projectRayFromCameraAndViewportCoordinate(scene->getActiveCamera(),inputEvent->touch.position);
             test->setPosition(r.origin.x, r.origin.y);
         } else if(inputEvent->getEventCode() == InputEvent::EVENT_TOUCHES_MOVED){
-            Logger::log("Touch moved: %f, %f", inputEvent->touch.position.x, inputEvent->touch.position.y);
             Ray r = scene->projectRayFromCameraAndViewportCoordinate(scene->getActiveCamera(),inputEvent->touch.position);
-            Logger::log("Origin: %f, %f", r.origin.x, r.origin.y);
             test->setPosition(r.origin.x, r.origin.y);
         }
     } else if(event->getEventType() == "CoreMotionEvent"){
         CoreMotionEvent* motionEvent = (CoreMotionEvent*)event;
         if(motionEvent->getEventCode() == Core::EVENT_ACCELEROMETER_MOTION){
-            //Logger::log("Acceleration: %f, %f, %f", motionEvent->amount.x, motionEvent->amount.y, motionEvent->amount.z);
-            //rot += motionEvent->amount;
-            //test->Translate(motionEvent->amount.y*0.01, motionEvent->amount.x*0.01);
         } else if (motionEvent->getEventCode() == Core::EVENT_GYRO_ROTATION){
-            //Logger::log("Rotation: %f, %f, %f", motionEvent->amount.x, motionEvent->amount.y, motionEvent->amount.z);
             test->Translate(motionEvent->amount.x*0.01, motionEvent->amount.y*0.01);
         }
     }
@@ -116,13 +116,9 @@ PolycodeTemplateApp::~PolycodeTemplateApp() {
 
 bool PolycodeTemplateApp::Update() {
     if (!core->paused) {
-        //Logger::log("update");
         running += core->getElapsed();
         if(running >= 1){
-            //core->deviceAttitude.
-            //Logger::log("Orientation: %f, %f, %f", , rot.y, rot.z);
             fps->setText(String::NumberToString(core->getFPS(),0));
-            //Logger::log("Backing: %f", Services()->getRenderer()->getBackingResolutionScaleX());
             running = 0;
         }
         return core->updateAndRender();

+ 3 - 0
build/android/TemplateApp/jni/PolycodeTemplateApp.h

@@ -30,4 +30,7 @@ private:
     Vector3 rot;
     Number running;
     SceneLabel* fps;
+
+    Scene* game;
+    ScenePrimitive* box;
 };

+ 5 - 1
include/polycode/view/android/PolycodeView.h

@@ -78,6 +78,8 @@ namespace Polycode {
 			
 			unsigned int lifecycleFlags;
 			bool isInteractable();
+			
+			jobject WakeLock;
     };
 }
 
@@ -110,4 +112,6 @@ static int sensorLoop(int fd, int events, void* data);
 void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize);
 void* startApp(void* data);
 
-int GetUnicodeChar(ANativeActivity* native_activity, int eventType, int keyCode, int metaState);
+int JNIGetUnicodeChar(ANativeActivity* native_activity, int eventType, int keyCode, int metaState);
+void JNIAutoHideNavBar(ANativeActivity* native_activity);
+void JNIWakeLock(ANativeActivity* native_activity, bool acquire);

+ 156 - 37
src/view/android/PolycodeView.cpp

@@ -51,13 +51,10 @@ PolycodeView::PolycodeView(ANativeActivity* native, String title){
 	native_activity->callbacks->onWindowFocusChanged 	= onWindowFocusChanged;
 	native_activity->callbacks->onInputQueueCreated		= onInputQueueCreated;
 	native_activity->callbacks->onInputQueueDestroyed	= onInputQueueDestroyed;
+	native_activity->callbacks->onSaveInstanceState		= onSaveInstanceState;
 	
 	native_config = AConfiguration_new();
 	AConfiguration_fromAssetManager(native_config, native_activity->assetManager);
-// 	AConfiguration* myConfig = AConfiguration_new();
-// 	AConfiguration_copy(myConfig, native_config);
-// 	AConfiguration_setNavHidden(myConfig,ACONFIGURATION_NAVHIDDEN_YES);
-// 	AConfiguration_setKeysHidden(native_config, ACONFIGURATION_KEYSHIDDEN_YES);
 	
 	sensorManager = ASensorManager_getInstance();		
 	
@@ -73,20 +70,18 @@ PolycodeView::PolycodeView(ANativeActivity* native, String title){
 	sensorQueue = ASensorManager_createEventQueue(sensorManager, looper, ALOOPER_POLL_CALLBACK, sensorLoop, this);
 	
 	if(accelerometer){
-// 		Logger::log("Accelerometer found. Name: %s, Vendor: %s", ASensor_getName(accelerometer), ASensor_getVendor(accelerometer));
 		ASensorEventQueue_enableSensor(sensorQueue, accelerometer);
 	}
 	
 	if(gyroscope){
-// 		Logger::log("Gyroscope found. Name: %s, Vendor: %s", ASensor_getName(gyroscope), ASensor_getVendor(gyroscope));
 		ASensorEventQueue_enableSensor(sensorQueue, gyroscope);
 	}
 	
 	if(orientation){
-// 		Logger::log("Orientation found. Name: %s, Vendor: %s", ASensor_getName(orientation), ASensor_getVendor(orientation));
  		ASensorEventQueue_enableSensor(sensorQueue, orientation);
 	}
 	gyroTimestamp = 0;
+	WakeLock = NULL;
 	
 }
 
@@ -103,6 +98,7 @@ void onStart(ANativeActivity* activity){
 
 void onResume(ANativeActivity* activity){
 	Logger::log("onResume");
+	JNIWakeLock(activity, true);
 	if(core)
 		core->paused = false;
 	((PolycodeView*)activity->instance)->lifecycleFlags |= APP_STATUS_ACTIVE;
@@ -110,6 +106,7 @@ void onResume(ANativeActivity* activity){
 
 void onPause(ANativeActivity* activity){
 	Logger::log("onPause");
+	JNIWakeLock(activity, false);
 	((PolycodeView*)activity->instance)->lifecycleFlags &= ~APP_STATUS_ACTIVE;
 }
 
@@ -123,6 +120,28 @@ void onStop(ANativeActivity* activity){
 
 void onDestroy(ANativeActivity* activity){
 	Logger::log("onDestroy");
+	JavaVM* javaVM = activity->vm;
+	JNIEnv* jniEnv;
+	bool attached = false;
+	
+	if(javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) ==JNI_EDETACHED){
+		JavaVMAttachArgs attachArgs;
+		attachArgs.version = JNI_VERSION_1_6;
+		attachArgs.name = "NativeThread";
+		attachArgs.group = NULL;
+		
+		jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
+		if(result == JNI_ERR){
+			if(core)
+				core->Shutdown();
+		}
+		attached = true;
+	}
+	jniEnv->DeleteGlobalRef(((PolycodeView*)activity->instance)->WakeLock);
+	
+	if(attached)
+		javaVM->DetachCurrentThread();
+	
 	if(core)
 		core->Shutdown();
 }
@@ -135,6 +154,7 @@ void onWindowFocusChanged(ANativeActivity* activity, int hasFocus){
 		event.eventCode = Core::EVENT_GAINED_FOCUS;
 		if(core)
 			core->handleSystemEvent(event);
+		JNIAutoHideNavBar(activity);
 		((PolycodeView*)activity->instance)->lifecycleFlags|=APP_STATUS_FOCUSED;
 	}else{
 		AndroidEvent event = AndroidEvent();
@@ -229,10 +249,10 @@ static int inputLoop(int fd, int events, void* data){
 				action = AKeyEvent_getAction(aev);
 				if(action == AKEY_EVENT_ACTION_DOWN){
 					event.eventCode = InputEvent::EVENT_KEYDOWN;
-					event.unicodeChar = GetUnicodeChar(((PolycodeView*)data)->native_activity, AKEY_EVENT_ACTION_DOWN, kC, AKeyEvent_getMetaState(aev));
+					event.unicodeChar = JNIGetUnicodeChar(((PolycodeView*)data)->native_activity, AKEY_EVENT_ACTION_DOWN, kC, AKeyEvent_getMetaState(aev));
 				} else if (action == AKEY_EVENT_ACTION_UP){
 					event.eventCode = InputEvent::EVENT_KEYUP;
-					event.unicodeChar = GetUnicodeChar(((PolycodeView*)data)->native_activity, AKEY_EVENT_ACTION_UP, kC, AKeyEvent_getMetaState(aev));
+					event.unicodeChar = JNIGetUnicodeChar(((PolycodeView*)data)->native_activity, AKEY_EVENT_ACTION_UP, kC, AKeyEvent_getMetaState(aev));
 				}
 
 			} else if(type == AINPUT_EVENT_TYPE_MOTION){
@@ -315,35 +335,26 @@ static int inputLoop(int fd, int events, void* data){
 }
 
 static int sensorLoop(int fd, int events, void* data){
-// 	Logger::log("sensorLoop");
 	ASensorEventQueue* queue = ((PolycodeView*)data)->sensorQueue;
 	
 	ASensorEvent event;
 	while(ASensorEventQueue_getEvents(queue, &event, 1) > 0){
-// 		Logger::log("event.type: %d", event.type);
 		float dT;
 		switch(event.type){
 			case ASENSOR_TYPE_ACCELEROMETER:
  				core->_setAcceleration(Vector3(event.acceleration.x,event.acceleration.y,event.acceleration.z));
-				//Logger::log("event.type: %d", event.type);
 				break;
 			case ASENSOR_TYPE_GYROSCOPE:
 				if(((PolycodeView*)data)->gyroTimestamp!=0){
 					dT = (event.timestamp-((PolycodeView*)data)->gyroTimestamp)/1000000000.0f;
 					core->_setGyroRotation(Vector3(event.vector.x*dT*TODEGREES, event.vector.y*dT*TODEGREES, event.vector.z*dT*TODEGREES));
-					//core->_setGyroRotation(Vector3(TODEGREES*(event.uncalibrated_gyro.x_uncalib - event.uncalibrated_gyro.x_bias), TODEGREES*(event.uncalibrated_gyro.y_uncalib - event.uncalibrated_gyro.y_bias), TODEGREES*(event.uncalibrated_gyro.z_uncalib - event.uncalibrated_gyro.z_bias)));
-					//Logger::log("event.type: %d", event.type);
 				}
 				((PolycodeView*)data)->gyroTimestamp = event.timestamp;
 				break;
 			case ASENSOR_TYPE_GAME_ROTATION_VECTOR:
-				
-//  				Logger::log("event: %f, %f, %f, %f", event.data[3],event.data[0],event.data[1],event.data[2]);
-				//q.fromAngleAxis(acos(event.data[3])*2, Vector3(event.data[0], event.data[1], event.data[2]));
 				Quaternion q=Quaternion(event.data[3],event.data[0],event.data[1],event.data[2]);
 				Vector3 vec = q.toEulerAngles();
-// 				Logger::log("event: %f, %f, %f", vec.x, vec.y, vec.z);
-//  				core->deviceAttitude = q;
+ 				core->deviceAttitude = q;
 				break;
 		}
 	}
@@ -377,13 +388,8 @@ void* startApp(void* data){
 	Logger::log("startApp");
 	startHelper* helper = (startHelper*)data;
 	
-// 	pthread_mutex_lock(&helper->mutex);
-//     helper->running = 1;
-//     pthread_cond_broadcast(&helper->cond);
-//     pthread_mutex_unlock(&helper->mutex);
-	
 	android_main(helper);
-	Logger::log("startApp4");
+	
     pthread_mutex_lock(&helper->mutex);
     if (((PolycodeView*)helper->activity->instance)->native_input != NULL) {
         AInputQueue_detachLooper(((PolycodeView*)helper->activity->instance)->native_input);
@@ -395,18 +401,22 @@ void* startApp(void* data){
 }
 
 //From http://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c
-int GetUnicodeChar(ANativeActivity* native_activity, int eventType, int keyCode, int metaState){
+int JNIGetUnicodeChar(ANativeActivity* native_activity, int eventType, int keyCode, int metaState){
 	JavaVM* javaVM = native_activity->vm;
-	JNIEnv* jniEnv = native_activity->env;
-
-	JavaVMAttachArgs attachArgs;
-	attachArgs.version = JNI_VERSION_1_6;
-	attachArgs.name = "NativeThread";
-	attachArgs.group = NULL;
+	JNIEnv* jniEnv;
+	bool attached = false;
 	
-	jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
-	if(result == JNI_ERR){
-		return 0;
+	if(javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) ==JNI_EDETACHED){
+		JavaVMAttachArgs attachArgs;
+		attachArgs.version = JNI_VERSION_1_6;
+		attachArgs.name = "NativeThread";
+		attachArgs.group = NULL;
+		
+		jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
+		if(result == JNI_ERR){
+			return 0;
+		}
+		attached = true;
 	}
 	
 	jclass class_key_event = jniEnv->FindClass("android/view/KeyEvent");
@@ -425,7 +435,116 @@ int GetUnicodeChar(ANativeActivity* native_activity, int eventType, int keyCode,
 
 		unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char, metaState);
 	}
-// 	javaVM->DetachCurrentThread();
+	
+	if(attached)
+		javaVM->DetachCurrentThread();
 
 	return unicodeKey;
-}
+}
+
+//From: http://www.gamedev.net/topic/674511-auto-hide-nav-bar-on-android-app-ndk/#entry5270901
+void JNIAutoHideNavBar(ANativeActivity* native_activity){
+	JavaVM* javaVM = native_activity->vm;
+	JNIEnv* jniEnv;
+	bool attached = false;
+	
+	if(javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) ==JNI_EDETACHED){
+		JavaVMAttachArgs attachArgs;
+		attachArgs.version = JNI_VERSION_1_6;
+		attachArgs.name = "NativeThread";
+		attachArgs.group = NULL;
+		
+		jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
+		if(result == JNI_ERR){
+			return;
+		}
+		attached = true;
+	}
+
+	jclass activityClass = jniEnv->FindClass("android/app/NativeActivity");
+	jmethodID getWindow = jniEnv->GetMethodID(activityClass, "getWindow", "()Landroid/view/Window;");
+
+	jclass windowClass = jniEnv->FindClass("android/view/Window");
+	jmethodID getDecorView = jniEnv->GetMethodID(windowClass, "getDecorView", "()Landroid/view/View;");
+
+	jclass viewClass = jniEnv->FindClass("android/view/View");
+	jmethodID setSystemUiVisibility = jniEnv->GetMethodID(viewClass, "setSystemUiVisibility", "(I)V");
+
+	jobject window = jniEnv->CallObjectMethod(native_activity->clazz, getWindow);
+
+	jobject decorView = jniEnv->CallObjectMethod(window, getDecorView);
+
+	jfieldID flagFullscreenID = jniEnv->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
+	jfieldID flagHideNavigationID = jniEnv->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I");
+	jfieldID flagImmersiveStickyID = jniEnv->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I");
+
+	int flagFullscreen = jniEnv->GetStaticIntField(viewClass, flagFullscreenID);
+	int flagHideNavigation = jniEnv->GetStaticIntField(viewClass, flagHideNavigationID);
+	int flagImmersiveSticky = jniEnv->GetStaticIntField(viewClass, flagImmersiveStickyID);
+
+	int flag = flagFullscreen | flagHideNavigation | flagImmersiveSticky;
+
+	jniEnv->CallVoidMethod(decorView, setSystemUiVisibility, flag);
+
+	if(attached)
+		javaVM->DetachCurrentThread();
+}
+
+void JNIWakeLock(ANativeActivity* native_activity, bool acquire){
+	JavaVM* javaVM = native_activity->vm;
+	JNIEnv* jniEnv;
+	bool attached = false;
+	
+	if(javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) ==JNI_EDETACHED){
+		JavaVMAttachArgs attachArgs;
+		attachArgs.version = JNI_VERSION_1_6;
+		attachArgs.name = "NativeThread";
+		attachArgs.group = NULL;
+		
+		jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
+		if(result == JNI_ERR){
+			return;
+		}
+		attached = true;
+	}
+
+	jobject WakeLock = ((PolycodeView*)native_activity->instance)->WakeLock;
+	
+	if(!WakeLock){
+		jclass classNativeActivity = jniEnv->FindClass("android/app/NativeActivity");
+		jclass classPowerManager = jniEnv->FindClass("android/os/PowerManager");
+		
+		jmethodID getSystemServiceID = jniEnv->GetMethodID(classNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+		jstring POWER_SERVICE = jniEnv->NewStringUTF("power");
+		jobject PowerManager = jniEnv->CallObjectMethod(native_activity->clazz, getSystemServiceID, POWER_SERVICE);
+		
+		jmethodID newWakeLockID = jniEnv->GetMethodID(classPowerManager, "newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;");
+		int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a;
+		long int ON_AFTER_RELEASE = 0x20000000;
+		jstring jWakeLockTag = jniEnv->NewStringUTF("Polycode");
+
+		WakeLock = jniEnv->CallObjectMethod(PowerManager, newWakeLockID, SCREEN_BRIGHT_WAKE_LOCK | ON_AFTER_RELEASE, jWakeLockTag);
+		
+		jniEnv->DeleteLocalRef(jWakeLockTag);
+		jniEnv->DeleteLocalRef(POWER_SERVICE);
+		
+		((PolycodeView*)native_activity->instance)->WakeLock = jniEnv->NewGlobalRef(WakeLock);
+	}
+	
+	jclass wakeLock = jniEnv->FindClass("android/os/PowerManager$WakeLock");
+
+	if (acquire){
+		jmethodID acquireWakeLockID = jniEnv->GetMethodID(wakeLock, "acquire", "()V");
+		jniEnv->CallVoidMethod(WakeLock, acquireWakeLockID);
+	} else {
+		jmethodID isHeldWakeLockID = jniEnv->GetMethodID(wakeLock, "isHeld", "()Z");
+		if(jniEnv->CallBooleanMethod(WakeLock, isHeldWakeLockID)){
+			jmethodID releaseWakeLockID = jniEnv->GetMethodID(wakeLock, "release", "()V");
+			jniEnv->CallVoidMethod(WakeLock, releaseWakeLockID);
+		}
+	}
+	
+	if(attached)
+		javaVM->DetachCurrentThread();
+}
+