[clutter/android-enter-leave: 7/29] android: add application API to show/hide virtual keyboard



commit ad90fd69a709facd2dda07471a8715e426e1d74d
Author: Lionel Landwerlin <llandwerlin gmail com>
Date:   Sun May 27 01:20:42 2012 +0100

    android: add application API to show/hide virtual keyboard

 clutter/Makefile.am                           |    2 +
 clutter/android/android_jni_utils.cpp         |  108 ++++++++++++++++++++++
 clutter/android/android_jni_utils.h           |   12 +++
 clutter/android/clutter-android-application.c |  118 ++++++++++++++++++-------
 clutter/android/clutter-android-application.h |    7 +-
 configure.ac                                  |    1 +
 6 files changed, 213 insertions(+), 35 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 08aca25..e9ae411 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -510,6 +510,7 @@ android_source_c = \
 	$(NULL)
 
 android_source_c_priv = \
+	$(srcdir)/android/android_jni_utils.cpp \
 	$(srcdir)/android/android_native_app_glue.c \
 	$(srcdir)/android/clutter-device-manager-android.c \
 	$(srcdir)/android/clutter-backend-android.c \
@@ -523,6 +524,7 @@ android_source_h = \
 	$(NULL)
 
 android_source_h_priv = \
+	$(srcdir)/android/android_jni_utils.h \
 	$(srcdir)/android/android_native_app_glue.h \
 	$(srcdir)/android/clutter-device-manager-android.h \
 	$(srcdir)/android/clutter-backend-android.h \
diff --git a/clutter/android/android_jni_utils.cpp b/clutter/android/android_jni_utils.cpp
new file mode 100644
index 0000000..d5c61d8
--- /dev/null
+++ b/clutter/android/android_jni_utils.cpp
@@ -0,0 +1,108 @@
+#include <jni.h>
+
+#include "android_native_app_glue.h"
+
+extern "C" jint
+_android_show_keyboard (struct android_app *mApplication, jboolean show, jint flags)
+{
+  jint lResult;
+  jint lFlags = flags;
+  jboolean pShow = show;
+
+  JavaVM* lJavaVM = mApplication->activity->vm;
+  JNIEnv* lJNIEnv;
+
+  bool attached = false;
+  switch (lJavaVM->GetEnv ((void**) &lJNIEnv, JNI_VERSION_1_6))
+    {
+    case JNI_OK:
+      break;
+    case JNI_EDETACHED:
+      if (lJavaVM->AttachCurrentThread (&lJNIEnv, NULL) != 0)
+        {
+          return 1;
+        }
+      attached = true;
+      break;
+    case JNI_EVERSION:
+      return 2;
+    }
+
+  // Retrieves NativeActivity.
+  jobject lNativeActivity = mApplication->activity->clazz;
+  jclass ClassNativeActivity = lJNIEnv->GetObjectClass (lNativeActivity);
+
+  // Retrieves Context.INPUT_METHOD_SERVICE.
+  jclass ClassContext = lJNIEnv->FindClass ("android/content/Context");
+  jfieldID FieldINPUT_METHOD_SERVICE =
+    lJNIEnv->GetStaticFieldID (ClassContext,
+                               "INPUT_METHOD_SERVICE",
+                               "Ljava/lang/String;");
+  jobject INPUT_METHOD_SERVICE =
+    lJNIEnv->GetStaticObjectField (ClassContext,
+                                   FieldINPUT_METHOD_SERVICE);
+  if (INPUT_METHOD_SERVICE == NULL)
+    return 4;
+
+  // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
+  jclass ClassInputMethodManager =
+    lJNIEnv->FindClass ("android/view/inputmethod/InputMethodManager");
+   jmethodID MethodGetSystemService =
+     lJNIEnv->GetMethodID (ClassNativeActivity, "getSystemService",
+                           "(Ljava/lang/String;)Ljava/lang/Object;");
+   jobject lInputMethodManager =
+     lJNIEnv->CallObjectMethod (lNativeActivity,
+                                MethodGetSystemService,
+                                INPUT_METHOD_SERVICE);
+
+   // Runs getWindow().getDecorView().
+   jmethodID MethodGetWindow = lJNIEnv->GetMethodID (ClassNativeActivity,
+                                                     "getWindow",
+                                                     "()Landroid/view/Window;");
+   jobject lWindow = lJNIEnv->CallObjectMethod (lNativeActivity,
+                                                MethodGetWindow);
+   jclass ClassWindow = lJNIEnv->FindClass ("android/view/Window");
+   jmethodID MethodGetDecorView = lJNIEnv->GetMethodID (ClassWindow,
+                                                        "getDecorView",
+                                                        "()Landroid/view/View;");
+   jobject lDecorView = lJNIEnv->CallObjectMethod (lWindow,
+                                                   MethodGetDecorView);
+
+   jint retval = 0;
+
+   if (pShow) {
+     // Runs lInputMethodManager.showSoftInput(...).
+     jmethodID MethodShowSoftInput =
+       lJNIEnv->GetMethodID (ClassInputMethodManager, "showSoftInput",
+                             "(Landroid/view/View;I)Z");
+     jboolean lResult = lJNIEnv->CallBooleanMethod (lInputMethodManager,
+                                                    MethodShowSoftInput,
+                                                    lDecorView, lFlags);
+
+     retval = lResult;
+   } else {
+     // Runs lWindow.getViewToken()
+     jclass ClassView = lJNIEnv->FindClass ("android/view/View");
+     jmethodID MethodGetWindowToken = lJNIEnv->GetMethodID (ClassView,
+                                                            "getWindowToken",
+                                                            "()Landroid/os/IBinder;");
+     jobject lBinder = lJNIEnv->CallObjectMethod (lDecorView,
+                                                  MethodGetWindowToken);
+
+     // lInputMethodManager.hideSoftInput(...).
+     jmethodID MethodHideSoftInput =
+       lJNIEnv->GetMethodID (ClassInputMethodManager,
+                             "hideSoftInputFromWindow",
+                             "(Landroid/os/IBinder;I)Z");
+     jboolean lRes = lJNIEnv->CallBooleanMethod (lInputMethodManager,
+                                                 MethodHideSoftInput,
+                                                 lBinder, lFlags);
+
+     retval = lRes;
+   }
+
+   // Finished with the JVM.
+   lJavaVM->DetachCurrentThread();
+
+   return retval;
+}
diff --git a/clutter/android/android_jni_utils.h b/clutter/android/android_jni_utils.h
new file mode 100644
index 0000000..c3b622d
--- /dev/null
+++ b/clutter/android/android_jni_utils.h
@@ -0,0 +1,12 @@
+#ifndef __ANDROID_JNI_UTILS_H__
+#define __ANDROID_JNI_UTILS_H__
+
+#include <jni.h>
+
+#include "android_native_app_glue.h"
+
+jint _android_show_keyboard (struct android_app *mApplication,
+                             jboolean show, jint flags);
+
+
+#endif /* __ANDROID_JNI_UTILS_H__ */
diff --git a/clutter/android/clutter-android-application.c b/clutter/android/clutter-android-application.c
index 30bb1bc..8a8ab53 100644
--- a/clutter/android/clutter-android-application.c
+++ b/clutter/android/clutter-android-application.c
@@ -25,7 +25,6 @@
 #include <stdlib.h>
 #include <config.h>
 
-#include <android_native_app_glue.h>
 #include <android/input.h>
 #include <android/window.h>
 
@@ -42,6 +41,9 @@
 #include "clutter-android-application-private.h"
 #include "clutter-stage-android.h"
 
+#include "android_native_app_glue.h"
+#include "android_jni_utils.h"
+
 G_DEFINE_TYPE (ClutterAndroidApplication,
                clutter_android_application,
                G_TYPE_OBJECT)
@@ -155,10 +157,6 @@ clutter_android_handle_cmd (struct android_app *app,
       //test_fini (data);
       break;
 
-    case APP_CMD_GAINED_FOCUS:
-      g_message ("command: GAINED_FOCUS");
-      break;
-
     case APP_CMD_WINDOW_RESIZED:
       g_message ("command: window resized!");
       if (app->window != NULL)
@@ -200,6 +198,10 @@ clutter_android_handle_cmd (struct android_app *app,
       g_message ("command: CONTENT_RECT_CHANGED");
       break;
 
+    case APP_CMD_GAINED_FOCUS:
+      g_message ("command: GAINED_FOCUS");
+      break;
+
     case APP_CMD_LOST_FOCUS:
       /* When our app loses focus, we stop monitoring the accelerometer.
        * This is to avoid consuming battery while not being used. */
@@ -225,9 +227,10 @@ clutter_android_handle_cmd (struct android_app *app,
 }
 
 static gboolean
-translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
+translate_motion_event (AInputEvent *a_event)
 {
   int32_t action;
+  ClutterEvent *event;
   ClutterDeviceManager *manager;
   ClutterInputDevice *pointer_device;
 
@@ -235,14 +238,13 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
   pointer_device =
     clutter_device_manager_get_core_device (manager,
                                             CLUTTER_POINTER_DEVICE);
-  _clutter_input_device_set_stage (pointer_device, event->any.stage);
 
   action = AMotionEvent_getAction (a_event);
 
   switch (action & AMOTION_EVENT_ACTION_MASK)
     {
     case AMOTION_EVENT_ACTION_DOWN:
-      event->button.type = event->type = CLUTTER_BUTTON_PRESS;
+      event = clutter_event_new (CLUTTER_BUTTON_PRESS);
       event->button.button = 1;
       event->button.click_count = 1;
       event->button.device = pointer_device;
@@ -252,7 +254,7 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
       break;
 
     case AMOTION_EVENT_ACTION_UP:
-      event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
+      event = clutter_event_new (CLUTTER_BUTTON_RELEASE);
       event->button.button = 1;
       event->button.click_count = 1;
       event->button.device = pointer_device;
@@ -262,7 +264,7 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
       break;
 
     case AMOTION_EVENT_ACTION_MOVE:
-      event->motion.type = event->type = CLUTTER_MOTION;
+      event = clutter_event_new (CLUTTER_MOTION);
       event->motion.device = pointer_device;
        /* TODO: Following line is a massive hack for touch screen */
       event->motion.modifier_state = CLUTTER_BUTTON1_MASK;
@@ -276,40 +278,64 @@ translate_motion_event (ClutterEvent *event, AInputEvent *a_event)
       return FALSE;
     }
 
+  event->any.stage =
+    clutter_stage_manager_get_default_stage (clutter_stage_manager_get_default ());
+  _clutter_input_device_set_stage (pointer_device, event->any.stage);
+
+  _clutter_event_push (event, FALSE);
+
   return TRUE;
 }
 
 static gboolean
-translate_key_event (ClutterEvent *event, AInputEvent *a_event)
+translate_key_event (AInputEvent *a_event)
 {
   int32_t state;
+  ClutterEvent *event;
+  ClutterDeviceManager *manager;
+  ClutterInputDevice *keyboard_device;
 
-  /* g_message ("\tbutton/motion event: (%.02lf,%0.2lf)", */
-  /*            AMotionEvent_getX (a_event, 0), */
-  /*            AMotionEvent_getY (a_event, 0)); */
+  g_message ("key event");
 
   state = AMotionEvent_getMetaState (a_event);
 
-  event->key.unicode_value = AKeyEvent_getKeyCode (a_event);
+  manager = clutter_device_manager_get_default ();
+  keyboard_device =
+    clutter_device_manager_get_core_device (manager,
+                                            CLUTTER_KEYBOARD_DEVICE);
+
+  g_message ("\tflags = %x keycode = %i",
+             AKeyEvent_getFlags (a_event),
+             AKeyEvent_getKeyCode (a_event));
 
   switch (state)
     {
     case AKEY_STATE_UP:
-      /* g_message ("\tkey release"); */
-      event->type = event->key.type = CLUTTER_KEY_RELEASE;
+      g_message ("\tkey release");
+      event = clutter_event_new (CLUTTER_KEY_RELEASE);
       break;
 
     case AKEY_STATE_DOWN:
     case AKEY_STATE_VIRTUAL: /* TODO: Should we synthetize release? */
-      /* g_message ("\tkey press"); */
-      event->type = event->key.type = CLUTTER_KEY_PRESS;
+      g_message ("\tkey press");
+      event = clutter_event_new (CLUTTER_KEY_PRESS);
       break;
 
     default:
-      /* g_message ("\tmeh? %i", state); */
+      g_message ("\tmeh? %i", state);
       return FALSE;
     }
 
+  event->key.unicode_value = AKeyEvent_getKeyCode (a_event);
+  g_message ("\tunicode value = %i", AKeyEvent_getKeyCode (a_event));
+  event->key.device = keyboard_device;
+
+  event->any.stage =
+    clutter_stage_manager_get_default_stage (clutter_stage_manager_get_default ());
+  _clutter_input_device_set_stage (keyboard_device, event->any.stage);
+
+  _clutter_event_push (event, FALSE);
+
   return TRUE;
 }
 
@@ -320,30 +346,19 @@ static int32_t
 clutter_android_handle_input (struct android_app *app,
                               AInputEvent        *a_event)
 {
-  ClutterEvent *event;
   gboolean process = FALSE;
 
   g_message ("input!");
 
-  event = clutter_event_new (CLUTTER_NOTHING);
-  event->any.stage =
-    clutter_stage_manager_get_default_stage (clutter_stage_manager_get_default ());
-
-  g_message ("plop!");
   if (AInputEvent_getType (a_event) == AINPUT_EVENT_TYPE_KEY)
     {
-      process = translate_key_event (event, a_event);
+      process = translate_key_event (a_event);
     }
   else if (AInputEvent_getType (a_event) == AINPUT_EVENT_TYPE_MOTION)
     {
-      process = translate_motion_event (event, a_event);
+      process = translate_motion_event (a_event);
     }
 
-  if (process)
-    _clutter_event_push (event, FALSE);
-  else
-    clutter_event_free (event);
-
   return (int32_t) process;
 }
 
@@ -375,6 +390,43 @@ clutter_android_application_get_asset_manager (ClutterAndroidApplication *applic
   return application->android_application->activity->assetManager;
 }
 
+void
+clutter_android_application_show_keyboard (ClutterAndroidApplication *application,
+                                           gboolean show_keyboard,
+                                           gboolean implicit)
+{
+  jint ret;
+
+  g_return_if_fail (CLUTTER_IS_ANDROID_APPLICATION (application));
+
+  if (show_keyboard)
+    {
+      g_message ("showing keyboard");
+      if (implicit)
+        ret = _android_show_keyboard (application->android_application,
+                                      JNI_TRUE,
+                                      ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
+      else
+        ret = _android_show_keyboard (application->android_application,
+                                      JNI_TRUE,
+                                      ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
+    }
+  else
+    {
+      g_message ("hiding keyboard");
+      if (implicit)
+        ret = _android_show_keyboard (application->android_application,
+                                JNI_FALSE,
+                                ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY);
+      else
+        ret = _android_show_keyboard (application->android_application,
+                                JNI_FALSE,
+                                ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS);
+    }
+
+  g_message ("THE FucK %i", ret);
+}
+
 /*
  * This is the main entry point of a native application that is using
  * android_native_app_glue.  It runs in its own thread, with its own
diff --git a/clutter/android/clutter-android-application.h b/clutter/android/clutter-android-application.h
index 07c77fa..bbf58fa 100644
--- a/clutter/android/clutter-android-application.h
+++ b/clutter/android/clutter-android-application.h
@@ -70,8 +70,11 @@ void clutter_android_main (ClutterAndroidApplication *application);
 
 GType clutter_android_application_get_type (void) G_GNUC_CONST;
 
-void              clutter_android_application_run                   (ClutterAndroidApplication *application);
-AAssetManager *   clutter_android_application_get_asset_manager     (ClutterAndroidApplication *application);
+void clutter_android_application_show_keyboard (ClutterAndroidApplication *application,
+                                                gboolean show_keyboard,
+                                                gboolean implicit);
+void clutter_android_application_run (ClutterAndroidApplication *application);
+AAssetManager *clutter_android_application_get_asset_manager (ClutterAndroidApplication *application);
 
 G_END_DECLS
 
diff --git a/configure.ac b/configure.ac
index 8e16fb6..c5811e0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -444,6 +444,7 @@ AS_IF([test "x$enable_android" = "xyes"],
         CLUTTER_INPUT_BACKENDS="$CLUTTER_INPUT_BACKENDS android"
 
         AC_DEFINE([HAVE_CLUTTER_ANDROID], [1], [Have the Android backend])
+        AC_PROG_CXX
 
         BACKEND_PC_FILES="$BACKEND_PC_FILES glib-android-1.0"
       ])



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]