aboutsummaryrefslogtreecommitdiff
path: root/examples/01.HelloWorld_Android/android_tools.cpp
blob: 758a0a8d918dd02cf0291f09d88be8bed471f327 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "android_tools.h"
#include <android/log.h>	// for the occasional debugging, style: __android_log_print(ANDROID_LOG_VERBOSE, "Irrlicht", "%s\n", "We do log");

namespace irr 
{
namespace android
{

// Not all DisplayMetrics are available through the NDK. 
// So we access the Java classes with the JNI interface.
// You can access other Java classes available in Android in similar ways.
// Function based roughly on the code from here: http://stackoverflow.com/questions/13249164/android-using-jni-from-nativeactivity
bool getDisplayMetrics(android_app* app, SDisplayMetrics & metrics)
{
	if (!app || !app->activity || !app->activity->vm )
		return false;
	
	JNIEnv* jni = 0;
	app->activity->vm->AttachCurrentThread(&jni, NULL);
	if (!jni )
		return false;

	
	// get all the classes we want to access from the JVM
	jclass classNativeActivity = jni->FindClass("android/app/NativeActivity");
	jclass classWindowManager = jni->FindClass("android/view/WindowManager");
	jclass classDisplay = jni->FindClass("android/view/Display");
	jclass classDisplayMetrics = jni->FindClass("android/util/DisplayMetrics");
	
	if (!classNativeActivity || !classWindowManager || !classDisplay || !classDisplayMetrics)
	{
		app->activity->vm->DetachCurrentThread();
		return false;
	}
	
	
	// Get all the methods we want to access from the JVM classes
	// Note: You can get the signatures (third parameter of GetMethodID) for all 
	// functions of a class with the javap tool, like in the following example for class DisplayMetrics:
	// javap -s -classpath myandroidpath/adt-bundle-linux-x86_64-20131030/sdk/platforms/android-10/android.jar android/util/DisplayMetrics
	jmethodID idNativeActivity_getWindowManager = jni->GetMethodID( classNativeActivity
												, "getWindowManager"
												, "()Landroid/view/WindowManager;"); 
	jmethodID idWindowManager_getDefaultDisplay = jni->GetMethodID( classWindowManager
												, "getDefaultDisplay"
												, "()Landroid/view/Display;");
	jmethodID idDisplayMetrics_constructor = jni->GetMethodID( classDisplayMetrics
														, "<init>"
														, "()V");
	jmethodID idDisplay_getMetrics = jni->GetMethodID( classDisplay
										 , "getMetrics"
										 , "(Landroid/util/DisplayMetrics;)V");
	
	if (!idNativeActivity_getWindowManager || !idWindowManager_getDefaultDisplay || !idDisplayMetrics_constructor 
		|| !idDisplay_getMetrics)
	{
		app->activity->vm->DetachCurrentThread();
		return false;
	}
	

	// In Java the following code would be: getWindowManager().getDefaultDisplay().getMetrics(metrics);
	// Note: If you need to call java functions in time-critical places you can split getting the jmethodID's 
	// and calling the functions into separate functions as you only have to get the jmethodID's once.
	jobject windowManager = jni->CallObjectMethod(app->activity->clazz, idNativeActivity_getWindowManager);
	
	if (!windowManager)
	{
		app->activity->vm->DetachCurrentThread();
		return false;
	}
	jobject display = jni->CallObjectMethod(windowManager, idWindowManager_getDefaultDisplay);
	if (!display)
	{
		app->activity->vm->DetachCurrentThread();
		return false;
	}
	jobject displayMetrics = jni->NewObject( classDisplayMetrics, idDisplayMetrics_constructor);
	if (!displayMetrics)
	{
		app->activity->vm->DetachCurrentThread();
		return false;
	}
	jni->CallVoidMethod(display, idDisplay_getMetrics, displayMetrics);
	
	// access the fields of DisplayMetrics (we ignore the DENSITY constants)
	jfieldID idDisplayMetrics_widthPixels = jni->GetFieldID( classDisplayMetrics, "widthPixels", "I");
	jfieldID idDisplayMetrics_heightPixels = jni->GetFieldID( classDisplayMetrics, "heightPixels", "I");
	jfieldID idDisplayMetrics_density = jni->GetFieldID( classDisplayMetrics, "density", "F");
	jfieldID idDisplayMetrics_densityDpi = jni->GetFieldID( classDisplayMetrics, "densityDpi", "I");
	jfieldID idDisplayMetrics_scaledDensity = jni->GetFieldID( classDisplayMetrics, "scaledDensity", "F");
	jfieldID idDisplayMetrics_xdpi = jni->GetFieldID(classDisplayMetrics, "xdpi", "F");
	jfieldID idDisplayMetrics_ydpi = jni->GetFieldID(classDisplayMetrics, "ydpi", "F");
	
	if ( idDisplayMetrics_widthPixels )
		metrics.widthPixels = jni->GetIntField(displayMetrics, idDisplayMetrics_widthPixels);
	if ( idDisplayMetrics_heightPixels )
		metrics.heightPixels = jni->GetIntField(displayMetrics, idDisplayMetrics_heightPixels);
	if (idDisplayMetrics_density )
		metrics.density = jni->GetFloatField(displayMetrics, idDisplayMetrics_density);
	if (idDisplayMetrics_densityDpi)
		metrics.densityDpi = jni->GetIntField(displayMetrics, idDisplayMetrics_densityDpi);
	if (idDisplayMetrics_scaledDensity)
		metrics.scaledDensity = jni->GetFloatField(displayMetrics, idDisplayMetrics_scaledDensity);
	if ( idDisplayMetrics_xdpi )
		metrics.xdpi = jni->GetFloatField(displayMetrics, idDisplayMetrics_xdpi);
	if ( idDisplayMetrics_ydpi )
		metrics.ydpi = jni->GetFloatField(displayMetrics, idDisplayMetrics_ydpi);
	
	app->activity->vm->DetachCurrentThread();
	return true;
}

void setSoftInputVisibility(android_app* app, bool visible)
{
	// NOTE: Unfortunately ANativeActivity_showSoftInput from the NDK does not work and Google does not care.
	// This is based on the solution from @Ratamovic from here: http://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity
	
	if (!app || !app->activity || !app->activity->vm )
		return;
	
	JNIEnv* jni = 0;
	app->activity->vm->AttachCurrentThread(&jni, NULL);
	if (!jni )
		return;
	
	// get all the classes we want to access from the JVM (could be cached)
	jclass classNativeActivity = jni->FindClass("android/app/NativeActivity");
	jclass classInputMethodManager = jni->FindClass("android/view/inputmethod/InputMethodManager");
	jclass classWindow = jni->FindClass("android/view/Window");
	jclass classView = jni->FindClass("android/view/View");
	
	if (classNativeActivity && classInputMethodManager && classWindow)
	{
		// Get all the methods we want to access from the JVM classes (could be cached)
		jmethodID mid_getSystemService = jni->GetMethodID(classNativeActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
		jmethodID mid_showSoftInput = jni->GetMethodID(classInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
		
		jmethodID mid_hideSoftInput = jni->GetMethodID(classInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z");
		jmethodID mid_getWindow = jni->GetMethodID(classNativeActivity, "getWindow", "()Landroid/view/Window;");
		jmethodID mid_getWindowToken = jni->GetMethodID(classView, "getWindowToken", "()Landroid/os/IBinder;");
		jmethodID mid_getDecorView = jni->GetMethodID(classWindow, "getDecorView", "()Landroid/view/View;");

		if ( mid_getSystemService && mid_showSoftInput && mid_hideSoftInput && mid_getWindow && mid_getDecorView && mid_getWindowToken )
		{
			jstring paramInput = jni->NewStringUTF("input_method"); 
			jobject objInputMethodManager = jni->CallObjectMethod(app->activity->clazz, mid_getSystemService, paramInput); 
			jni->DeleteLocalRef(paramInput); 

			jobject objWindow = jni->CallObjectMethod(app->activity->clazz, mid_getWindow);
			
			if ( visible && objInputMethodManager && objWindow)
			{
				jobject objDecorView = jni->CallObjectMethod(objWindow, mid_getDecorView);				
				if ( objDecorView )
				{
					int showFlags = 0;
					jni->CallBooleanMethod(objInputMethodManager, mid_showSoftInput, objDecorView, showFlags);
				}
			}
			else if ( !visible && objInputMethodManager && objWindow )
			{
				jobject objDecorView = jni->CallObjectMethod(objWindow, mid_getDecorView);				
				if ( objDecorView )
				{
					jobject objBinder = jni->CallObjectMethod(objDecorView, mid_getWindowToken);							
					if ( objBinder )
					{
						int hideFlags = 0;
						jni->CallBooleanMethod(objInputMethodManager, mid_hideSoftInput, objBinder, hideFlags);
					}
				}
			}
		}
	}
	
	app->activity->vm->DetachCurrentThread();	
}

} // namespace android
} // namespace irr