Skip to content

Commit 3fd2463

Browse files
committed
android: add application API to show/hide virtual keyboard
1 parent 34916de commit 3fd2463

6 files changed

+213
-35
lines changed

clutter/Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ android_source_c = \
537537
$(NULL)
538538

539539
android_source_c_priv = \
540+
$(srcdir)/android/android_jni_utils.cpp \
540541
$(srcdir)/android/android_native_app_glue.c \
541542
$(srcdir)/android/clutter-device-manager-android.c \
542543
$(srcdir)/android/clutter-backend-android.c \
@@ -550,6 +551,7 @@ android_source_h = \
550551
$(NULL)
551552

552553
android_source_h_priv = \
554+
$(srcdir)/android/android_jni_utils.h \
553555
$(srcdir)/android/android_native_app_glue.h \
554556
$(srcdir)/android/clutter-device-manager-android.h \
555557
$(srcdir)/android/clutter-backend-android.h \

clutter/android/android_jni_utils.cpp

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#include <jni.h>
2+
3+
#include "android_native_app_glue.h"
4+
5+
extern "C" jint
6+
_android_show_keyboard (struct android_app *mApplication, jboolean show, jint flags)
7+
{
8+
jint lResult;
9+
jint lFlags = flags;
10+
jboolean pShow = show;
11+
12+
JavaVM* lJavaVM = mApplication->activity->vm;
13+
JNIEnv* lJNIEnv;
14+
15+
bool attached = false;
16+
switch (lJavaVM->GetEnv ((void**) &lJNIEnv, JNI_VERSION_1_6))
17+
{
18+
case JNI_OK:
19+
break;
20+
case JNI_EDETACHED:
21+
if (lJavaVM->AttachCurrentThread (&lJNIEnv, NULL) != 0)
22+
{
23+
return 1;
24+
}
25+
attached = true;
26+
break;
27+
case JNI_EVERSION:
28+
return 2;
29+
}
30+
31+
// Retrieves NativeActivity.
32+
jobject lNativeActivity = mApplication->activity->clazz;
33+
jclass ClassNativeActivity = lJNIEnv->GetObjectClass (lNativeActivity);
34+
35+
// Retrieves Context.INPUT_METHOD_SERVICE.
36+
jclass ClassContext = lJNIEnv->FindClass ("android/content/Context");
37+
jfieldID FieldINPUT_METHOD_SERVICE =
38+
lJNIEnv->GetStaticFieldID (ClassContext,
39+
"INPUT_METHOD_SERVICE",
40+
"Ljava/lang/String;");
41+
jobject INPUT_METHOD_SERVICE =
42+
lJNIEnv->GetStaticObjectField (ClassContext,
43+
FieldINPUT_METHOD_SERVICE);
44+
if (INPUT_METHOD_SERVICE == NULL)
45+
return 4;
46+
47+
// Runs getSystemService(Context.INPUT_METHOD_SERVICE).
48+
jclass ClassInputMethodManager =
49+
lJNIEnv->FindClass ("android/view/inputmethod/InputMethodManager");
50+
jmethodID MethodGetSystemService =
51+
lJNIEnv->GetMethodID (ClassNativeActivity, "getSystemService",
52+
"(Ljava/lang/String;)Ljava/lang/Object;");
53+
jobject lInputMethodManager =
54+
lJNIEnv->CallObjectMethod (lNativeActivity,
55+
MethodGetSystemService,
56+
INPUT_METHOD_SERVICE);
57+
58+
// Runs getWindow().getDecorView().
59+
jmethodID MethodGetWindow = lJNIEnv->GetMethodID (ClassNativeActivity,
60+
"getWindow",
61+
"()Landroid/view/Window;");
62+
jobject lWindow = lJNIEnv->CallObjectMethod (lNativeActivity,
63+
MethodGetWindow);
64+
jclass ClassWindow = lJNIEnv->FindClass ("android/view/Window");
65+
jmethodID MethodGetDecorView = lJNIEnv->GetMethodID (ClassWindow,
66+
"getDecorView",
67+
"()Landroid/view/View;");
68+
jobject lDecorView = lJNIEnv->CallObjectMethod (lWindow,
69+
MethodGetDecorView);
70+
71+
jint retval = 0;
72+
73+
if (pShow) {
74+
// Runs lInputMethodManager.showSoftInput(...).
75+
jmethodID MethodShowSoftInput =
76+
lJNIEnv->GetMethodID (ClassInputMethodManager, "showSoftInput",
77+
"(Landroid/view/View;I)Z");
78+
jboolean lResult = lJNIEnv->CallBooleanMethod (lInputMethodManager,
79+
MethodShowSoftInput,
80+
lDecorView, lFlags);
81+
82+
retval = lResult;
83+
} else {
84+
// Runs lWindow.getViewToken()
85+
jclass ClassView = lJNIEnv->FindClass ("android/view/View");
86+
jmethodID MethodGetWindowToken = lJNIEnv->GetMethodID (ClassView,
87+
"getWindowToken",
88+
"()Landroid/os/IBinder;");
89+
jobject lBinder = lJNIEnv->CallObjectMethod (lDecorView,
90+
MethodGetWindowToken);
91+
92+
// lInputMethodManager.hideSoftInput(...).
93+
jmethodID MethodHideSoftInput =
94+
lJNIEnv->GetMethodID (ClassInputMethodManager,
95+
"hideSoftInputFromWindow",
96+
"(Landroid/os/IBinder;I)Z");
97+
jboolean lRes = lJNIEnv->CallBooleanMethod (lInputMethodManager,
98+
MethodHideSoftInput,
99+
lBinder, lFlags);
100+
101+
retval = lRes;
102+
}
103+
104+
// Finished with the JVM.
105+
lJavaVM->DetachCurrentThread();
106+
107+
return retval;
108+
}

clutter/android/android_jni_utils.h

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef __ANDROID_JNI_UTILS_H__
2+
#define __ANDROID_JNI_UTILS_H__
3+
4+
#include <jni.h>
5+
6+
#include "android_native_app_glue.h"
7+
8+
jint _android_show_keyboard (struct android_app *mApplication,
9+
jboolean show, jint flags);
10+
11+
12+
#endif /* __ANDROID_JNI_UTILS_H__ */

clutter/android/clutter-android-application.c

+85-33
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
#include <stdlib.h>
2626
#include <config.h>
2727

28-
#include <android_native_app_glue.h>
2928
#include <android/input.h>
3029
#include <android/window.h>
3130

@@ -42,6 +41,9 @@
4241
#include "clutter-android-application-private.h"
4342
#include "clutter-stage-android.h"
4443

44+
#include "android_native_app_glue.h"
45+
#include "android_jni_utils.h"
46+
4547
G_DEFINE_TYPE (ClutterAndroidApplication,
4648
clutter_android_application,
4749
G_TYPE_OBJECT)
@@ -155,10 +157,6 @@ clutter_android_handle_cmd (struct android_app *app,
155157
//test_fini (data);
156158
break;
157159

158-
case APP_CMD_GAINED_FOCUS:
159-
g_message ("command: GAINED_FOCUS");
160-
break;
161-
162160
case APP_CMD_WINDOW_RESIZED:
163161
g_message ("command: window resized!");
164162
if (app->window != NULL)
@@ -200,6 +198,10 @@ clutter_android_handle_cmd (struct android_app *app,
200198
g_message ("command: CONTENT_RECT_CHANGED");
201199
break;
202200

201+
case APP_CMD_GAINED_FOCUS:
202+
g_message ("command: GAINED_FOCUS");
203+
break;
204+
203205
case APP_CMD_LOST_FOCUS:
204206
/* When our app loses focus, we stop monitoring the accelerometer.
205207
* This is to avoid consuming battery while not being used. */
@@ -225,24 +227,24 @@ clutter_android_handle_cmd (struct android_app *app,
225227
}
226228

227229
static gboolean
228-
translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
230+
translate_motion_event (AInputEvent *a_event)
229231
{
230232
int32_t action;
233+
ClutterEvent *event;
231234
ClutterDeviceManager *manager;
232235
ClutterInputDevice *pointer_device;
233236

234237
manager = clutter_device_manager_get_default ();
235238
pointer_device =
236239
clutter_device_manager_get_core_device (manager,
237240
CLUTTER_POINTER_DEVICE);
238-
_clutter_input_device_set_stage (pointer_device, event->any.stage);
239241

240242
action = AMotionEvent_getAction (a_event);
241243

242244
switch (action & AMOTION_EVENT_ACTION_MASK)
243245
{
244246
case AMOTION_EVENT_ACTION_DOWN:
245-
event->button.type = event->type = CLUTTER_BUTTON_PRESS;
247+
event = clutter_event_new (CLUTTER_BUTTON_PRESS);
246248
event->button.button = 1;
247249
event->button.click_count = 1;
248250
event->button.device = pointer_device;
@@ -252,7 +254,7 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
252254
break;
253255

254256
case AMOTION_EVENT_ACTION_UP:
255-
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
257+
event = clutter_event_new (CLUTTER_BUTTON_RELEASE);
256258
event->button.button = 1;
257259
event->button.click_count = 1;
258260
event->button.device = pointer_device;
@@ -262,7 +264,7 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
262264
break;
263265

264266
case AMOTION_EVENT_ACTION_MOVE:
265-
event->motion.type = event->type = CLUTTER_MOTION;
267+
event = clutter_event_new (CLUTTER_MOTION);
266268
event->motion.device = pointer_device;
267269
/* TODO: Following line is a massive hack for touch screen */
268270
event->motion.modifier_state = CLUTTER_BUTTON1_MASK;
@@ -276,40 +278,64 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
276278
return FALSE;
277279
}
278280

281+
event->any.stage =
282+
clutter_stage_manager_get_default_stage (clutter_stage_manager_get_default ());
283+
_clutter_input_device_set_stage (pointer_device, event->any.stage);
284+
285+
_clutter_event_push (event, FALSE);
286+
279287
return TRUE;
280288
}
281289

282290
static gboolean
283-
translate_key_event (ClutterEvent *event, AInputEvent *a_event)
291+
translate_key_event (AInputEvent *a_event)
284292
{
285293
int32_t state;
294+
ClutterEvent *event;
295+
ClutterDeviceManager *manager;
296+
ClutterInputDevice *keyboard_device;
286297

287-
/* g_message ("\tbutton/motion event: (%.02lf,%0.2lf)", */
288-
/* AMotionEvent_getX (a_event, 0), */
289-
/* AMotionEvent_getY (a_event, 0)); */
298+
g_message ("key event");
290299

291300
state = AMotionEvent_getMetaState (a_event);
292301

293-
event->key.unicode_value = AKeyEvent_getKeyCode (a_event);
302+
manager = clutter_device_manager_get_default ();
303+
keyboard_device =
304+
clutter_device_manager_get_core_device (manager,
305+
CLUTTER_KEYBOARD_DEVICE);
306+
307+
g_message ("\tflags = %x keycode = %i",
308+
AKeyEvent_getFlags (a_event),
309+
AKeyEvent_getKeyCode (a_event));
294310

295311
switch (state)
296312
{
297313
case AKEY_STATE_UP:
298-
/* g_message ("\tkey release"); */
299-
event->type = event->key.type = CLUTTER_KEY_RELEASE;
314+
g_message ("\tkey release");
315+
event = clutter_event_new (CLUTTER_KEY_RELEASE);
300316
break;
301317

302318
case AKEY_STATE_DOWN:
303319
case AKEY_STATE_VIRTUAL: /* TODO: Should we synthetize release? */
304-
/* g_message ("\tkey press"); */
305-
event->type = event->key.type = CLUTTER_KEY_PRESS;
320+
g_message ("\tkey press");
321+
event = clutter_event_new (CLUTTER_KEY_PRESS);
306322
break;
307323

308324
default:
309-
/* g_message ("\tmeh? %i", state); */
325+
g_message ("\tmeh? %i", state);
310326
return FALSE;
311327
}
312328

329+
event->key.unicode_value = AKeyEvent_getKeyCode (a_event);
330+
g_message ("\tunicode value = %i", AKeyEvent_getKeyCode (a_event));
331+
event->key.device = keyboard_device;
332+
333+
event->any.stage =
334+
clutter_stage_manager_get_default_stage (clutter_stage_manager_get_default ());
335+
_clutter_input_device_set_stage (keyboard_device, event->any.stage);
336+
337+
_clutter_event_push (event, FALSE);
338+
313339
return TRUE;
314340
}
315341

@@ -320,30 +346,19 @@ static int32_t
320346
clutter_android_handle_input (struct android_app *app,
321347
AInputEvent *a_event)
322348
{
323-
ClutterEvent *event;
324349
gboolean process = FALSE;
325350

326351
g_message ("input!");
327352

328-
event = clutter_event_new (CLUTTER_NOTHING);
329-
event->any.stage =
330-
clutter_stage_manager_get_default_stage (clutter_stage_manager_get_default ());
331-
332-
g_message ("plop!");
333353
if (AInputEvent_getType (a_event) == AINPUT_EVENT_TYPE_KEY)
334354
{
335-
process = translate_key_event (event, a_event);
355+
process = translate_key_event (a_event);
336356
}
337357
else if (AInputEvent_getType (a_event) == AINPUT_EVENT_TYPE_MOTION)
338358
{
339-
process = translate_motion_event (event, a_event);
359+
process = translate_motion_event (a_event);
340360
}
341361

342-
if (process)
343-
_clutter_event_push (event, FALSE);
344-
else
345-
clutter_event_free (event);
346-
347362
return (int32_t) process;
348363
}
349364

@@ -375,6 +390,43 @@ clutter_android_application_get_asset_manager (ClutterAndroidApplication *applic
375390
return application->android_application->activity->assetManager;
376391
}
377392

393+
void
394+
clutter_android_application_show_keyboard (ClutterAndroidApplication *application,
395+
gboolean show_keyboard,
396+
gboolean implicit)
397+
{
398+
jint ret;
399+
400+
g_return_if_fail (CLUTTER_IS_ANDROID_APPLICATION (application));
401+
402+
if (show_keyboard)
403+
{
404+
g_message ("showing keyboard");
405+
if (implicit)
406+
ret = _android_show_keyboard (application->android_application,
407+
JNI_TRUE,
408+
ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
409+
else
410+
ret = _android_show_keyboard (application->android_application,
411+
JNI_TRUE,
412+
ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
413+
}
414+
else
415+
{
416+
g_message ("hiding keyboard");
417+
if (implicit)
418+
ret = _android_show_keyboard (application->android_application,
419+
JNI_FALSE,
420+
ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY);
421+
else
422+
ret = _android_show_keyboard (application->android_application,
423+
JNI_FALSE,
424+
ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS);
425+
}
426+
427+
g_message ("THE FucK %i", ret);
428+
}
429+
378430
/*
379431
* This is the main entry point of a native application that is using
380432
* android_native_app_glue. It runs in its own thread, with its own

clutter/android/clutter-android-application.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ void clutter_android_main (ClutterAndroidApplication *application);
7070

7171
GType clutter_android_application_get_type (void) G_GNUC_CONST;
7272

73-
void clutter_android_application_run (ClutterAndroidApplication *application);
74-
AAssetManager * clutter_android_application_get_asset_manager (ClutterAndroidApplication *application);
73+
void clutter_android_application_show_keyboard (ClutterAndroidApplication *application,
74+
gboolean show_keyboard,
75+
gboolean implicit);
76+
void clutter_android_application_run (ClutterAndroidApplication *application);
77+
AAssetManager *clutter_android_application_get_asset_manager (ClutterAndroidApplication *application);
7578

7679
G_END_DECLS
7780

configure.ac

+1
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ AS_IF([test "x$enable_android" = "xyes"],
444444
CLUTTER_INPUT_BACKENDS="$CLUTTER_INPUT_BACKENDS android"
445445
446446
AC_DEFINE([HAVE_CLUTTER_ANDROID], [1], [Have the Android backend])
447+
AC_PROG_CXX
447448
448449
BACKEND_PC_FILES="$BACKEND_PC_FILES glib-android-1.0"
449450
])

0 commit comments

Comments
 (0)