VR: Dynamically load GVR Keyboard implementation
This CL add the necessary glue to wire up the keyboard API to its
implementation in the Daydream Keyboard APK.
The keyboard can be temporarily be summoned via the app button by
turning on the vr-browser-keyboard runtime flag. Follow-up CLs will
introduce the keyboard element and remove the temporary code in
VrShellGl.
Bug: 641470
Change-Id: I3d1372aa335db69c1e38adbb00a67993f9eea1e0
Reviewed-on: https://chromium-review.googlesource.com/731568
Commit-Queue: Yash Malik <[email protected]>
Reviewed-by: Yaron Friedman <[email protected]>
Reviewed-by: Biao She <[email protected]>
Reviewed-by: Amirhossein Simjour <[email protected]>
Reviewed-by: Max Moroz <[email protected]>
Reviewed-by: Ian Vollick <[email protected]>
Cr-Commit-Position: refs/heads/master@{#517638}
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index ebf6130..41df90c7 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -312,6 +312,7 @@
java_files += chrome_vr_java_sources
deps += [
"//device/vr:java",
+ "//third_party/gvr-android-keyboard:kb_java",
"//third_party/gvr-android-sdk:gvr_common_java",
]
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/BuildConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/BuildConstants.java
new file mode 100644
index 0000000..c91b06f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/BuildConstants.java
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.keyboard;
+
+/**
+ * Miscellaneous build-related constants.
+ */
+public class BuildConstants {
+ /**
+ * The version number of the keyboard API. A local copy of this class should
+ * be built into both the client and the SDK. Then at SDK load time, the
+ * version numbers can be compared to make sure the client and SDK have
+ * compatible APIs.
+ */
+ public static final long API_VERSION = 1;
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/GvrKeyboardLoaderClient.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/GvrKeyboardLoaderClient.java
new file mode 100644
index 0000000..a7358ff
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/GvrKeyboardLoaderClient.java
@@ -0,0 +1,160 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.keyboard;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.google.vr.keyboard.IGvrKeyboardLoader;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/** Loads the GVR keyboard SDK dynamically using the Keyboard Service. */
+@JNINamespace("vr_shell")
+public class GvrKeyboardLoaderClient {
+ private static final String TAG = "ChromeGvrKbClient";
+ private static final boolean DEBUG_LOGS = false;
+
+ private static final String KEYBOARD_PACKAGE = "com.google.android.vr.inputmethod";
+ private static final String LOADER_NAME = "com.google.vr.keyboard.GvrKeyboardLoader";
+
+ private static IGvrKeyboardLoader sLoader = null;
+ private static ClassLoader sRemoteClassLoader = null;
+
+ @CalledByNative
+ public static long loadKeyboardSDK() {
+ if (DEBUG_LOGS) Log.i(TAG, "loadKeyboardSDK");
+ IGvrKeyboardLoader loader = getLoader();
+ if (loader == null) {
+ if (DEBUG_LOGS) Log.i(TAG, "Couldn't find GVR keyboard SDK.");
+ return 0;
+ }
+ try {
+ long handle = loader.loadGvrKeyboard(BuildConstants.API_VERSION);
+ return handle;
+ } catch (RemoteException e) {
+ if (DEBUG_LOGS) Log.i(TAG, "Couldn't load GVR keyboard SDK.");
+ return 0;
+ }
+ }
+
+ @CalledByNative
+ public static void closeKeyboardSDK(long handle) {
+ if (DEBUG_LOGS) Log.i(TAG, "loadKeyboardSDK");
+ IGvrKeyboardLoader loader = getLoader();
+ if (loader != null) {
+ try {
+ loader.closeGvrKeyboard(handle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't close GVR keyboard library", e);
+ }
+ }
+ }
+
+ private static IGvrKeyboardLoader getLoader() {
+ if (sLoader == null) {
+ ClassLoader remoteClassLoader = (ClassLoader) getRemoteClassLoader();
+ if (remoteClassLoader != null) {
+ IBinder binder = newBinder(remoteClassLoader, LOADER_NAME);
+ sLoader = IGvrKeyboardLoader.Stub.asInterface(binder);
+ }
+ }
+ return sLoader;
+ }
+
+ private static Context getRemoteContext(Context context) {
+ try {
+ // The flags Context.CONTEXT_INCLUDE_CODE and Context.CONTEXT_IGNORE_SECURITY are
+ // needed to be able to load classes via the classloader of the returned context.
+ return context.createPackageContext(KEYBOARD_PACKAGE,
+ Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Couldn't find remote context", e);
+ }
+ return null;
+ }
+
+ @CalledByNative
+ public static Context getContextWrapper() {
+ Context context = ContextUtils.getApplicationContext();
+ return new KeyboardContextWrapper(getRemoteContext(context), context);
+ }
+
+ @CalledByNative
+ public static Object getRemoteClassLoader() {
+ Context context = ContextUtils.getApplicationContext();
+ if (sRemoteClassLoader == null) {
+ Context remoteContext = getRemoteContext(context);
+ if (remoteContext != null) {
+ sRemoteClassLoader = remoteContext.getClassLoader();
+ }
+ }
+ return sRemoteClassLoader;
+ }
+
+ private static IBinder newBinder(ClassLoader classLoader, String className) {
+ try {
+ Class<?> clazz = classLoader.loadClass(className);
+ return (IBinder) clazz.getConstructor().newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Unable to find dynamic class " + className);
+ } catch (InstantiationException e) {
+ throw new IllegalStateException("Unable to instantiate the remote class " + className);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(
+ "Unable to call the default constructor of " + className);
+ } catch (Exception e) {
+ throw new IllegalStateException("Reflection error in " + className);
+ }
+ }
+
+ private static class KeyboardContextWrapper extends ContextWrapper {
+ private final Context mKeyboardContext;
+
+ private KeyboardContextWrapper(Context keyboardContext, Context baseContext) {
+ super(baseContext);
+ this.mKeyboardContext = keyboardContext;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ // As the LAYOUT_INFLATER_SERVICE uses assets from the Context, it should point to the
+ // keyboard Context.
+ if (Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
+ return mKeyboardContext.getSystemService(name);
+ } else {
+ return super.getSystemService(name);
+ }
+ }
+
+ @Override
+ public Resources getResources() {
+ return mKeyboardContext.getResources();
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ return mKeyboardContext.getAssets();
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return mKeyboardContext.getClassLoader();
+ }
+ }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 5184586..bb96d92 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1402,6 +1402,8 @@
"java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrWindowAndroid.java",
"java/src/org/chromium/chrome/browser/vr_shell/OnDispatchTouchEventCallback.java",
+ "java/src/org/chromium/chrome/browser/vr_shell/keyboard/BuildConstants.java",
+ "java/src/org/chromium/chrome/browser/vr_shell/keyboard/GvrKeyboardLoaderClient.java",
]
chrome_test_java_sources = [
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1aeed0b1..ffd4559 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2176,6 +2176,9 @@
{"vr-browsing", flag_descriptions::kVrBrowsingName,
flag_descriptions::kVrBrowsingDescription, kOsAndroid,
FEATURE_VALUE_TYPE(features::kVrBrowsing)},
+ {"vr-browser-keyboard", flag_descriptions::kVrBrowserKeyboardName,
+ flag_descriptions::kVrBrowserKeyboardDescription, kOsAndroid,
+ FEATURE_VALUE_TYPE(features::kVrBrowserKeyboard)},
{"vr-browsing-experimental-features",
flag_descriptions::kVrBrowsingExperimentalFeaturesName,
flag_descriptions::kVrBrowsingExperimentalFeaturesDescription, kOsAndroid,
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 816920a..4e51490 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -20,6 +20,7 @@
"autocomplete_controller.cc",
"autocomplete_controller.h",
"gl_browser_interface.h",
+ "gvr_keyboard_shim.cc",
"gvr_util.cc",
"gvr_util.h",
"mailbox_to_surface_bridge.cc",
@@ -75,7 +76,10 @@
"android",
]
- configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
+ configs += [
+ "//third_party/gvr-android-keyboard:kb_config",
+ "//third_party/gvr-android-sdk:libgvr_config",
+ ]
}
generate_jni("vr_shell_jni_headers") {
@@ -85,6 +89,7 @@
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
+ "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/GvrKeyboardLoaderClient.java",
]
jni_package = "vr_shell"
}
diff --git a/chrome/browser/android/vr_shell/DEPS b/chrome/browser/android/vr_shell/DEPS
index 1a4cc31..59f29a44 100644
--- a/chrome/browser/android/vr_shell/DEPS
+++ b/chrome/browser/android/vr_shell/DEPS
@@ -2,5 +2,6 @@
"+cc/base",
"+cc/layers",
"+device/vr",
+ "+third_party/gvr-android-keyboard/src",
"+third_party/gvr-android-sdk/src",
]
diff --git a/chrome/browser/android/vr_shell/gvr_keyboard_shim.cc b/chrome/browser/android/vr_shell/gvr_keyboard_shim.cc
new file mode 100644
index 0000000..cdc68151
--- /dev/null
+++ b/chrome/browser/android/vr_shell/gvr_keyboard_shim.cc
@@ -0,0 +1,246 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/gvr-android-keyboard/src/libraries/headers/vr/gvr/capi/include/gvr_keyboard.h"
+
+#include <android/native_window_jni.h>
+#include <dlfcn.h>
+#include <jni.h>
+#include <cmath>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/logging.h"
+#include "jni/GvrKeyboardLoaderClient_jni.h"
+
+// Run CALL macro for every function defined in the API.
+#define FOR_EACH_API_FN \
+ CALL(gvr_keyboard_initialize) \
+ CALL(gvr_keyboard_create) \
+ CALL(gvr_keyboard_get_input_mode) \
+ CALL(gvr_keyboard_set_input_mode) \
+ CALL(gvr_keyboard_get_recommended_world_from_keyboard_matrix) \
+ CALL(gvr_keyboard_set_world_from_keyboard_matrix) \
+ CALL(gvr_keyboard_show) \
+ CALL(gvr_keyboard_update_button_state) \
+ CALL(gvr_keyboard_update_controller_ray) \
+ CALL(gvr_keyboard_get_text) \
+ CALL(gvr_keyboard_set_text) \
+ CALL(gvr_keyboard_get_selection_indices) \
+ CALL(gvr_keyboard_set_selection_indices) \
+ CALL(gvr_keyboard_get_composing_indices) \
+ CALL(gvr_keyboard_set_composing_indices) \
+ CALL(gvr_keyboard_set_frame_time) \
+ CALL(gvr_keyboard_set_eye_from_world_matrix) \
+ CALL(gvr_keyboard_set_projection_matrix) \
+ CALL(gvr_keyboard_set_viewport) \
+ CALL(gvr_keyboard_advance_frame) \
+ CALL(gvr_keyboard_render) \
+ CALL(gvr_keyboard_hide) \
+ CALL(gvr_keyboard_destroy)
+
+namespace {
+
+// Declare implementation function pointers.
+#define CALL(fn) decltype(&fn) impl_##fn = nullptr;
+FOR_EACH_API_FN
+#undef CALL
+
+template <typename Fn>
+bool LoadFunction(void* handle, const char* function_name, Fn* fn_out) {
+ void* fn = dlsym(handle, function_name);
+ if (!fn) {
+ LOG(ERROR) << "Failed to load " << function_name
+ << " from GVR keyboard library: " << dlerror();
+ return false;
+ }
+ *fn_out = reinterpret_cast<Fn>(fn);
+ return true;
+}
+
+static void* sdk_handle = nullptr;
+
+void CloseSdk() {
+ if (!sdk_handle)
+ return;
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ CHECK(env);
+
+ vr_shell::Java_GvrKeyboardLoaderClient_closeKeyboardSDK(
+ env, reinterpret_cast<jlong>(sdk_handle));
+
+// Null all the function pointers.
+#define CALL(fn) impl_##fn = nullptr;
+ FOR_EACH_API_FN
+#undef CALL
+
+ sdk_handle = nullptr;
+}
+
+bool LoadSdk(void* closure, gvr_keyboard_callback callback) {
+ if (sdk_handle)
+ return true;
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ CHECK(env);
+
+ base::android::ScopedJavaLocalRef<jobject> context_wrapper =
+ vr_shell::Java_GvrKeyboardLoaderClient_getContextWrapper(env);
+
+ base::android::ScopedJavaLocalRef<jobject> remote_class_loader =
+ vr_shell::Java_GvrKeyboardLoaderClient_getRemoteClassLoader(env);
+
+ sdk_handle = reinterpret_cast<void*>(
+ vr_shell::Java_GvrKeyboardLoaderClient_loadKeyboardSDK(env));
+
+ if (!sdk_handle) {
+ LOG(ERROR) << "Failed to load GVR keyboard SDK.";
+ return false;
+ }
+
+ // Load all function pointers from the SDK.
+ bool success = true;
+#define CALL(fn) success &= LoadFunction(sdk_handle, #fn, &impl_##fn);
+ FOR_EACH_API_FN
+#undef CALL
+
+ if (!success) {
+ CloseSdk();
+ return false;
+ }
+
+ gvr_keyboard_initialize(env, context_wrapper.obj(),
+ remote_class_loader.obj());
+
+ return success;
+}
+
+} // namespace
+
+void gvr_keyboard_initialize(JNIEnv* env,
+ jobject app_context,
+ jobject class_loader) {
+ impl_gvr_keyboard_initialize(env, app_context, class_loader);
+}
+
+gvr_keyboard_context* gvr_keyboard_create(void* closure,
+ gvr_keyboard_callback callback) {
+ if (!LoadSdk(closure, callback)) {
+ return nullptr;
+ }
+ return impl_gvr_keyboard_create(closure, callback);
+}
+
+void gvr_keyboard_destroy(gvr_keyboard_context** context) {
+ impl_gvr_keyboard_destroy(context);
+ CloseSdk();
+}
+
+int32_t gvr_keyboard_get_input_mode(gvr_keyboard_context* context) {
+ return impl_gvr_keyboard_get_input_mode(context);
+}
+
+void gvr_keyboard_set_input_mode(gvr_keyboard_context* context,
+ int32_t input_mode) {
+ impl_gvr_keyboard_set_input_mode(context, input_mode);
+}
+
+void gvr_keyboard_get_recommended_world_from_keyboard_matrix(
+ float distance_from_eye,
+ gvr_mat4f* matrix) {
+ impl_gvr_keyboard_get_recommended_world_from_keyboard_matrix(
+ distance_from_eye, matrix);
+}
+
+void gvr_keyboard_set_world_from_keyboard_matrix(gvr_keyboard_context* context,
+ const gvr_mat4f* matrix) {
+ impl_gvr_keyboard_set_world_from_keyboard_matrix(context, matrix);
+}
+
+void gvr_keyboard_show(gvr_keyboard_context* context) {
+ impl_gvr_keyboard_show(context);
+}
+
+void gvr_keyboard_update_button_state(gvr_keyboard_context* context,
+ int32_t button_index,
+ bool pressed) {
+ impl_gvr_keyboard_update_button_state(context, button_index, pressed);
+}
+
+bool gvr_keyboard_update_controller_ray(gvr_keyboard_context* context,
+ const gvr_vec3f* start,
+ const gvr_vec3f* end,
+ gvr_vec3f* hit) {
+ return impl_gvr_keyboard_update_controller_ray(context, start, end, hit);
+}
+
+char* gvr_keyboard_get_text(gvr_keyboard_context* context) {
+ return impl_gvr_keyboard_get_text(context);
+}
+
+void gvr_keyboard_set_text(gvr_keyboard_context* context, const char* text) {
+ return impl_gvr_keyboard_set_text(context, text);
+}
+
+void gvr_keyboard_get_selection_indices(gvr_keyboard_context* context,
+ size_t* start,
+ size_t* end) {
+ impl_gvr_keyboard_get_selection_indices(context, start, end);
+}
+
+void gvr_keyboard_set_selection_indices(gvr_keyboard_context* context,
+ size_t start,
+ size_t end) {
+ impl_gvr_keyboard_set_selection_indices(context, start, end);
+}
+
+void gvr_keyboard_get_composing_indices(gvr_keyboard_context* context,
+ size_t* start,
+ size_t* end) {
+ impl_gvr_keyboard_get_composing_indices(context, start, end);
+}
+
+void gvr_keyboard_set_composing_indices(gvr_keyboard_context* context,
+ size_t start,
+ size_t end) {
+ impl_gvr_keyboard_set_composing_indices(context, start, end);
+}
+
+void gvr_keyboard_set_frame_time(gvr_keyboard_context* context,
+ const gvr_clock_time_point* time) {
+ impl_gvr_keyboard_set_frame_time(context, time);
+}
+
+void gvr_keyboard_set_eye_from_world_matrix(gvr_keyboard_context* context,
+ int32_t eye_type,
+ const gvr_mat4f* matrix) {
+ impl_gvr_keyboard_set_eye_from_world_matrix(context, eye_type, matrix);
+}
+
+void gvr_keyboard_set_projection_matrix(gvr_keyboard_context* context,
+ int32_t eye_type,
+ const gvr_mat4f* projection) {
+ impl_gvr_keyboard_set_projection_matrix(context, eye_type, projection);
+}
+
+void gvr_keyboard_set_viewport(gvr_keyboard_context* context,
+ int32_t eye_type,
+ const gvr_recti* viewport) {
+ impl_gvr_keyboard_set_viewport(context, eye_type, viewport);
+}
+
+void gvr_keyboard_advance_frame(gvr_keyboard_context* context) {
+ impl_gvr_keyboard_advance_frame(context);
+}
+
+void gvr_keyboard_render(gvr_keyboard_context* context, int32_t eye_type) {
+ impl_gvr_keyboard_render(context, eye_type);
+}
+
+void gvr_keyboard_hide(gvr_keyboard_context* context) {
+ impl_gvr_keyboard_hide(context);
+}
+
+#undef FOR_EACH_API_FN
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 1572c5e..4d10ce9 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -101,6 +101,46 @@
static constexpr float kRedrawSceneAngleDeltaDegrees = 1.0;
+static gvr_keyboard_context* keyboard_context;
+
+// TODO(ymalik,crbug.com/780318): This callback is temporary until we have an
+// editable input field.
+void OnKeyboardEvent(void*, int32_t event) {
+ switch (event) {
+ case GVR_KEYBOARD_ERROR_UNKNOWN:
+ LOG(ERROR) << "Unknown GVR keyboard error.";
+ break;
+ case GVR_KEYBOARD_ERROR_SERVICE_NOT_CONNECTED:
+ LOG(ERROR) << "GVR keyboard service not connected.";
+ break;
+ case GVR_KEYBOARD_ERROR_NO_LOCALES_FOUND:
+ LOG(ERROR) << "No GVR keyboard locales found.";
+ break;
+ case GVR_KEYBOARD_ERROR_SDK_LOAD_FAILED:
+ LOG(ERROR) << "GVR keyboard sdk load failed.";
+ break;
+ case GVR_KEYBOARD_SHOWN:
+ DVLOG(1) << "GVR keyboard shown.";
+ break;
+ case GVR_KEYBOARD_HIDDEN:
+ DVLOG(1) << "GVR keyboard hidden.";
+ break;
+ case GVR_KEYBOARD_TEXT_UPDATED: {
+ char* text = gvr_keyboard_get_text(keyboard_context);
+ DVLOG(1) << "GVR keyboard text updated: " << text;
+ free(reinterpret_cast<void*>(text));
+ } break;
+ case GVR_KEYBOARD_TEXT_COMMITTED: {
+ char* text = gvr_keyboard_get_text(keyboard_context);
+ DVLOG(1) << "GVR keyboard text updated: " << text;
+ free(reinterpret_cast<void*>(text));
+ gvr_keyboard_set_text(keyboard_context, "");
+ } break;
+ default:
+ NOTREACHED();
+ }
+}
+
gfx::Transform PerspectiveMatrixFromView(const gvr::Rectf& fov,
float z_near,
float z_far) {
@@ -196,6 +236,9 @@
VrShellGl::~VrShellGl() {
ClosePresentationBindings();
+ if (keyboard_enabled_) {
+ gvr_keyboard_destroy(&gvr_keyboard_);
+ }
}
void VrShellGl::Initialize() {
@@ -465,6 +508,7 @@
void VrShellGl::InitializeRenderer() {
gvr_api_->InitializeGl();
+ CreateKeyboard();
gfx::Transform head_pose;
device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose);
webvr_head_pose_.assign(kPoseRingBufferSize, head_pose);
@@ -614,6 +658,7 @@
controller_model.opacity = controller_->GetOpacity();
controller_model.laser_direction = controller_direction;
controller_model.laser_origin = laser_origin;
+ controller_model_ = controller_model;
vr::ReticleModel reticle_model;
ui_->input_manager()->HandleInput(current_time, controller_model,
@@ -645,6 +690,18 @@
if (controller_->ButtonUpHappened(
gvr::ControllerButton::GVR_CONTROLLER_BUTTON_APP)) {
+ // TODO(ymalik,crbug.com/780318): We temporarily show and hide the keyboard
+ // when the app button is pressed. This behavior is behind a runtime enabled
+ // feature and should go away as soon as we have editable input fields.
+ show_keyboard_ = keyboard_enabled_ && !show_keyboard_;
+ if (keyboard_enabled_) {
+ if (show_keyboard_) {
+ gvr_keyboard_show(gvr_keyboard_);
+ } else {
+ gvr_keyboard_hide(gvr_keyboard_);
+ }
+ }
+
// A gesture is a movement of the controller while holding the App button.
// If the angle of the movement is within a threshold, the action is
// considered a regular click
@@ -892,6 +949,10 @@
// screen showing in WebVR mode that must also fill the screen.
ui_->ui_renderer()->Draw(render_info_primary_);
+ // Draw keyboard. TODO(ymalik,crbug.com/780135): Keyboard should be a UI
+ // element and this special rendering logic should move out of here.
+ DrawKeyboard();
+
content_frame_available_ = false;
acquired_frame_.Unbind();
@@ -970,6 +1031,77 @@
}
}
+void VrShellGl::CreateKeyboard() {
+ if (gvr_keyboard_)
+ return;
+
+ keyboard_enabled_ =
+ base::FeatureList::IsEnabled(features::kVrBrowserKeyboard);
+ if (!keyboard_enabled_)
+ return;
+
+ gvr_keyboard_ = gvr_keyboard_create(nullptr, OnKeyboardEvent);
+ if (!gvr_keyboard_) {
+ keyboard_enabled_ = false;
+ return;
+ }
+ keyboard_context = gvr_keyboard_;
+
+ gvr_mat4f matrix;
+ gvr_keyboard_get_recommended_world_from_keyboard_matrix(2.0f, &matrix);
+ gvr_keyboard_set_world_from_keyboard_matrix(gvr_keyboard_, &matrix);
+}
+
+void VrShellGl::DrawKeyboard() {
+ if (!keyboard_enabled_)
+ return;
+
+ // Note that according to the keyboard API, these functions must be called
+ // every frame after the keyboard is created to process events, regardless of
+ // keyboard visibility.
+ gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
+ gvr_keyboard_set_frame_time(gvr_keyboard_, &target_time);
+ gvr_keyboard_advance_frame(gvr_keyboard_);
+
+ if (!show_keyboard_)
+ return;
+
+ bool pressed = controller_->ButtonUpHappened(
+ gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK);
+ gvr_keyboard_update_button_state(
+ gvr_keyboard_, gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK,
+ pressed);
+
+ gvr_vec3f start;
+ start.x = controller_model_.laser_origin.x();
+ start.y = controller_model_.laser_origin.y();
+ start.z = controller_model_.laser_origin.z();
+ gvr_vec3f end;
+ end.x = start.x + controller_model_.laser_direction.x();
+ end.y = start.y + controller_model_.laser_direction.y();
+ end.z = start.z + controller_model_.laser_direction.z();
+ gvr_vec3f hit_point;
+ gvr_keyboard_update_controller_ray(gvr_keyboard_, &start, &end, &hit_point);
+ for (auto eye : {GVR_LEFT_EYE, GVR_RIGHT_EYE}) {
+ vr::CameraModel& eye_info = (eye == GVR_LEFT_EYE)
+ ? render_info_primary_.left_eye_model
+ : render_info_primary_.right_eye_model;
+ gvr::Mat4f view_matrix;
+ TransformToGvrMat(eye_info.view_matrix, &view_matrix);
+ gvr_keyboard_set_eye_from_world_matrix(gvr_keyboard_, eye, &view_matrix);
+
+ gvr::Mat4f proj_matrix;
+ TransformToGvrMat(eye_info.proj_matrix, &proj_matrix);
+ gvr_keyboard_set_projection_matrix(gvr_keyboard_, eye, &proj_matrix);
+
+ gfx::Rect viewport_rect = eye_info.viewport;
+ const gvr::Recti viewport = {viewport_rect.x(), viewport_rect.right(),
+ viewport_rect.y(), viewport_rect.bottom()};
+ gvr_keyboard_set_viewport(gvr_keyboard_, eye, &viewport);
+ gvr_keyboard_render(gvr_keyboard_, eye);
+ }
+}
+
void VrShellGl::DrawFrameSubmitWhenReady(
int16_t frame_index,
const gfx::Transform& head_pose,
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 3c2f21a..1f6fa1e 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -24,6 +24,7 @@
#include "chrome/browser/vr/ui_renderer.h"
#include "device/vr/vr_service.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/gvr-android-keyboard/src/libraries/headers/vr/gvr/capi/include/gvr_keyboard.h"
#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
#include "ui/gfx/geometry/quaternion.h"
@@ -85,6 +86,8 @@
void OnTriggerEvent();
void OnPause();
void OnResume();
+ void DrawKeyboard();
+ void CreateKeyboard();
base::WeakPtr<vr::BrowserUiInterface> GetBrowserUiWeakPtr();
@@ -221,6 +224,7 @@
bool is_exiting_ = false;
std::unique_ptr<VrController> controller_;
+ gvr_keyboard_context* gvr_keyboard_ = nullptr;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
@@ -264,6 +268,10 @@
bool skips_redraw_when_not_dirty_;
gfx::Transform last_used_head_pose_;
+ bool keyboard_enabled_ = false;
+ bool show_keyboard_ = false;
+ vr::ControllerModel controller_model_;
+
base::WeakPtrFactory<VrShellGl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(VrShellGl);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8d9ef79d..b1177b8 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2718,6 +2718,10 @@
const char kVrBrowsingDescription[] =
"Browsing within a VR headset if available for this device.";
+const char kVrBrowserKeyboardName[] = "Chrome VR virtual keyboard.";
+const char kVrBrowserKeyboardDescription[] =
+ "Enable a virtual keyboard for Chrome VR.";
+
const char kVrBrowsingExperimentalFeaturesName[] =
"VR browsing experimental features";
const char kVrBrowsingExperimentalFeaturesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1850a7b0..135b31d5 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1679,6 +1679,9 @@
extern const char kVrBrowsingName[];
extern const char kVrBrowsingDescription[];
+extern const char kVrBrowserKeyboardName[];
+extern const char kVrBrowserKeyboardDescription[];
+
extern const char kVrBrowsingExperimentalFeaturesName[];
extern const char kVrBrowsingExperimentalFeaturesDescription[];
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 0bafcbc..8579f0c 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -227,6 +227,10 @@
#endif // BUILDFLAG(ENABLE_VR) || defined(OS_ANDROID)
#if BUILDFLAG(ENABLE_VR)
+// Enables the virtual keyboard for Chrome VR.
+const base::Feature kVrBrowserKeyboard{"VrBrowserKeyboard",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls features related to VR browsing that are under development.
const base::Feature kVrBrowsingExperimentalFeatures{
"VrBrowsingExperimentalFeatures", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 98866ad..6c57dcd1 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -118,6 +118,7 @@
extern const base::Feature kVrBrowsing;
#endif
#if BUILDFLAG(ENABLE_VR)
+extern const base::Feature kVrBrowserKeyboard;
extern const base::Feature kVrBrowsingExperimentalFeatures;
extern const base::Feature kVrBrowsingExperimentalRendering;
#endif
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c7f21ca..a71fabe 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -24565,6 +24565,7 @@
<int value="-1262152606" label="disable-lock-screen-apps"/>
<int value="-1261263046"
label="RemoveUsageOfDeprecatedGaiaSigninEndpoint:disabled"/>
+ <int value="-1259901957" label="VrBrowserKeyboard:disabled"/>
<int value="-1254070521" label="enable-slimming-paint-invalidation"/>
<int value="-1251411236" label="disable-new-md-input-view"/>
<int value="-1248478422" label="enable-zip-archiver-packer"/>
@@ -25588,6 +25589,7 @@
<int value="1612871297" label="WebPayments:disabled"/>
<int value="1612974229" label="allow-insecure-localhost"/>
<int value="1617187093" label="enable-improved-a2hs"/>
+ <int value="1621298798" label="VrBrowserKeyboard:enabled"/>
<int value="1622131033" label="ozone-test-single-overlay-support"/>
<int value="1626824478" label="ExperimentalAppBanners:disabled"/>
<int value="1630988998" label="VrBrowsingExperimentalRendering:disabled"/>