Commit e94965a9 authored by Tibor Goldschwendt's avatar Tibor Goldschwendt Committed by Commit Bot

[vr] Add Java API to request exiting VR from other Chrome code.

- This API can be used to exit VR when we encounter an unsupported
  feature.
- Make unhandled page info request use this API.

Change-Id: I974466cc40241f570aecb1ac08bb15b510bd57d2
Reviewed-on: https://chromium-review.googlesource.com/578209Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Commit-Queue: Tibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#488454}
parent 6cdda3e6
...@@ -336,6 +336,7 @@ java_cpp_enum("chrome_android_java_enums_srcjar") { ...@@ -336,6 +336,7 @@ java_cpp_enum("chrome_android_java_enums_srcjar") {
java_cpp_enum("chrome_vr_android_java_enums_srcjar") { java_cpp_enum("chrome_vr_android_java_enums_srcjar") {
sources = [ sources = [
"//chrome/browser/android/vr_shell/vr_core_info.h", "//chrome/browser/android/vr_shell/vr_core_info.h",
"//chrome/browser/vr/ui_unsupported_mode.h",
] ]
} }
......
// 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;
/**
* Callback to be invoked when a VR exit request was processed.
*/
public interface OnExitVrRequestListener {
/**
* Called if the exit VR request was successful and VR was exited.
*/
void onSucceeded();
/**
* Called if the exit request was denied (e.g. user
* chose to not exit VR).
*/
void onDenied();
}
\ No newline at end of file
...@@ -59,4 +59,9 @@ public interface VrShell { ...@@ -59,4 +59,9 @@ public interface VrShell {
* Returns whether the back button is enabled. * Returns whether the back button is enabled.
*/ */
Boolean isBackButtonEnabled(); Boolean isBackButtonEnabled();
/**
* Requests to exit VR.
*/
void requestToExitVr(@UiUnsupportedMode int reason);
} }
...@@ -51,7 +51,6 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivity; ...@@ -51,7 +51,6 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.help.HelpAndFeedback; import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.infobar.InfoBarIdentifier; import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder; import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder;
import org.chromium.chrome.browser.page_info.PageInfoPopup;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.util.IntentUtils;
...@@ -157,9 +156,10 @@ public class VrShellDelegate ...@@ -157,9 +156,10 @@ public class VrShellDelegate
private Boolean mInVrAtChromeLaunch; private Boolean mInVrAtChromeLaunch;
private boolean mShowingDaydreamDoff; private boolean mShowingDaydreamDoff;
private boolean mDoffOptional; private boolean mDoffOptional;
// Whether we should show the PageInfo UI. This is shown when we force exit the user // Listener to be called once we exited VR due to to an unsupported mode, e.g. the user clicked
// out of VR when they attempt to view the PageInfo. // the URL bar security icon.
private boolean mShouldShowPageInfo; private OnExitVrRequestListener mOnExitVrRequestListener;
private boolean mExitedDueToUnsupportedMode = false;
private boolean mExitingCct; private boolean mExitingCct;
private boolean mPaused; private boolean mPaused;
private int mRestoreSystemUiVisibilityFlag = -1; private int mRestoreSystemUiVisibilityFlag = -1;
...@@ -383,6 +383,16 @@ public class VrShellDelegate ...@@ -383,6 +383,16 @@ public class VrShellDelegate
sInstance.showDoffAndExitVrInternal(optional); sInstance.showDoffAndExitVrInternal(optional);
} }
public static void requestToExitVr(
OnExitVrRequestListener listener, @UiUnsupportedMode int reason) {
assert listener != null;
if (sInstance == null) {
listener.onDenied();
return;
}
sInstance.requestToExitVrInternal(listener, reason);
}
@CalledByNative @CalledByNative
private static VrShellDelegate getInstance() { private static VrShellDelegate getInstance() {
Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
...@@ -619,7 +629,6 @@ public class VrShellDelegate ...@@ -619,7 +629,6 @@ public class VrShellDelegate
case ActivityState.RESUMED: case ActivityState.RESUMED:
if (mInVr && activity != mActivity) { if (mInVr && activity != mActivity) {
if (mShowingDaydreamDoff) { if (mShowingDaydreamDoff) {
mShouldShowPageInfo = false;
onExitVrResult(true); onExitVrResult(true);
} else { } else {
shutdownVr(true /* disableVrMode */, false /* canReenter */, shutdownVr(true /* disableVrMode */, false /* canReenter */,
...@@ -784,7 +793,7 @@ public class VrShellDelegate ...@@ -784,7 +793,7 @@ public class VrShellDelegate
mVrDaydreamApi.launchVrHomescreen(); mVrDaydreamApi.launchVrHomescreen();
return; return;
} }
mShouldShowPageInfo = false; mExitedDueToUnsupportedMode = false;
shutdownNonPresentingNativeContext(); shutdownNonPresentingNativeContext();
// Lock orientation to landscape after enter VR. // Lock orientation to landscape after enter VR.
...@@ -1045,6 +1054,18 @@ public class VrShellDelegate ...@@ -1045,6 +1054,18 @@ public class VrShellDelegate
return ENTER_VR_REQUESTED; return ENTER_VR_REQUESTED;
} }
private void requestToExitVrInternal(
OnExitVrRequestListener listener, @UiUnsupportedMode int reason) {
assert listener != null;
// If we are currently processing another request or we are not in VR, deny the request.
if (sInstance.mOnExitVrRequestListener != null || !sInstance.mInVr) {
listener.onDenied();
return;
}
mOnExitVrRequestListener = listener;
mVrShell.requestToExitVr(reason);
}
@CalledByNative @CalledByNative
private boolean exitWebVRPresent() { private boolean exitWebVRPresent() {
if (!mInVr) return false; if (!mInVr) return false;
...@@ -1206,10 +1227,8 @@ public class VrShellDelegate ...@@ -1206,10 +1227,8 @@ public class VrShellDelegate
if (!mDoffOptional && !success && showDoff(false /* optional */)) return; if (!mDoffOptional && !success && showDoff(false /* optional */)) return;
mShowingDaydreamDoff = false; mShowingDaydreamDoff = false;
callOnExitVrRequestListener(success);
if (success) { if (success) {
if (mShouldShowPageInfo) {
sInstance.showPageInfoPopup();
}
shutdownVr(true /* disableVrMode */, false /* canReenter */, shutdownVr(true /* disableVrMode */, false /* canReenter */,
!mExitingCct /* stayingInChrome */); !mExitingCct /* stayingInChrome */);
if (mExitingCct) ((CustomTabActivity) mActivity).finishAndClose(false); if (mExitingCct) ((CustomTabActivity) mActivity).finishAndClose(false);
...@@ -1308,10 +1327,18 @@ public class VrShellDelegate ...@@ -1308,10 +1327,18 @@ public class VrShellDelegate
promptForFeedbackIfNeeded(stayingInChrome); promptForFeedbackIfNeeded(stayingInChrome);
if (stayingInChrome) createNonPresentingNativeContext(); if (stayingInChrome) createNonPresentingNativeContext();
// We don't want to show the PageInfo prompt if we return to Chrome assert mOnExitVrRequestListener == null;
// after shutting down for reasons other than a successful DOFF (e.g. }
// clicking the controller home button and returning to Chrome).
mShouldShowPageInfo = false; private void callOnExitVrRequestListener(boolean success) {
if (mOnExitVrRequestListener != null) {
if (success) {
mOnExitVrRequestListener.onSucceeded();
} else {
mOnExitVrRequestListener.onDenied();
}
}
mOnExitVrRequestListener = null;
} }
private void showDoffAndExitVrInternal(boolean optional) { private void showDoffAndExitVrInternal(boolean optional) {
...@@ -1320,9 +1347,14 @@ public class VrShellDelegate ...@@ -1320,9 +1347,14 @@ public class VrShellDelegate
shutdownVr(true /* disableVrMode */, false /* canReenter */, true /* stayingInChrome */); shutdownVr(true /* disableVrMode */, false /* canReenter */, true /* stayingInChrome */);
} }
/* package */ void onUnhandledPageInfo() { /* package */ void onExitVrRequestResult(boolean shouldExit) {
mShouldShowPageInfo = true; assert mOnExitVrRequestListener != null;
if (shouldExit) {
mExitedDueToUnsupportedMode = true;
showDoffAndExitVrInternal(true); showDoffAndExitVrInternal(true);
} else {
callOnExitVrRequestListener(false);
}
} }
/* package */ void exitCct() { /* package */ void exitCct() {
...@@ -1339,15 +1371,6 @@ public class VrShellDelegate ...@@ -1339,15 +1371,6 @@ public class VrShellDelegate
} }
} }
private void showPageInfoPopup() {
assert mShouldShowPageInfo;
// Note: we don't set mShouldShowPageInfo to false here because we don't
// want to show the feedback prompt when the user exits VR to view PageInfo. So this gets
// reset in shutdownVr.
PageInfoPopup.show(
mActivity, mActivity.getActivityTab(), null, PageInfoPopup.OPENED_FROM_VR);
}
private static void startFeedback(Tab tab) { private static void startFeedback(Tab tab) {
// TODO(ymalik): This call will connect to the Google Services api which can be slow. Can we // TODO(ymalik): This call will connect to the Google Services api which can be slow. Can we
// connect to it beforehand when we know that we'll be prompting for feedback? // connect to it beforehand when we know that we'll be prompting for feedback?
...@@ -1396,7 +1419,7 @@ public class VrShellDelegate ...@@ -1396,7 +1419,7 @@ public class VrShellDelegate
if (!stayingInChrome) return; if (!stayingInChrome) return;
if (VrFeedbackStatus.getFeedbackOptOut()) return; if (VrFeedbackStatus.getFeedbackOptOut()) return;
if (!mVrBrowserUsed) return; if (!mVrBrowserUsed) return;
if (mShouldShowPageInfo) return; if (mExitedDueToUnsupportedMode) return;
int exitCount = VrFeedbackStatus.getUserExitedAndEntered2DCount(); int exitCount = VrFeedbackStatus.getUserExitedAndEntered2DCount();
VrFeedbackStatus.setUserExitedAndEntered2DCount((exitCount + 1) % mFeedbackFrequency); VrFeedbackStatus.setUserExitedAndEntered2DCount((exitCount + 1) % mFeedbackFrequency);
......
...@@ -33,6 +33,7 @@ import org.chromium.chrome.browser.ChromeActivity; ...@@ -33,6 +33,7 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.NativePage; import org.chromium.chrome.browser.NativePage;
import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.page_info.PageInfoPopup;
import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.InterceptNavigationDelegateImpl; import org.chromium.chrome.browser.tab.InterceptNavigationDelegateImpl;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
...@@ -451,7 +452,16 @@ public class VrShellImpl ...@@ -451,7 +452,16 @@ public class VrShellImpl
// the security icon in the URL bar. // the security icon in the URL bar.
@CalledByNative @CalledByNative
public void onUnhandledPageInfo() { public void onUnhandledPageInfo() {
mDelegate.onUnhandledPageInfo(); mDelegate.requestToExitVr(new OnExitVrRequestListener() {
@Override
public void onSucceeded() {
PageInfoPopup.show(
mActivity, mActivity.getActivityTab(), null, PageInfoPopup.OPENED_FROM_VR);
}
@Override
public void onDenied() {}
}, UiUnsupportedMode.UNHANDLED_PAGE_INFO);
} }
// Exits CCT, returning to the app that opened it. // Exits CCT, returning to the app that opened it.
...@@ -652,6 +662,19 @@ public class VrShellImpl ...@@ -652,6 +662,19 @@ public class VrShellImpl
return mDelegate.hasDaydreamSupport(); return mDelegate.hasDaydreamSupport();
} }
@Override
public void requestToExitVr(@UiUnsupportedMode int reason) {
nativeRequestToExitVr(mNativeVrShell, reason);
}
@CalledByNative
private void onExitVrRequestResult(@UiUnsupportedMode int reason, boolean shouldExit) {
if (shouldExit) {
nativeLogUnsupportedModeUserMetric(mNativeVrShell, reason);
}
mDelegate.onExitVrRequestResult(shouldExit);
}
@CalledByNative @CalledByNative
private void showTab(int id) { private void showTab(int id) {
Tab tab = mActivity.getTabModelSelector().getTabById(id); Tab tab = mActivity.getTabModelSelector().getTabById(id);
...@@ -780,4 +803,7 @@ public class VrShellImpl ...@@ -780,4 +803,7 @@ public class VrShellImpl
private native void nativeRestoreContentSurface(long nativeVrShell); private native void nativeRestoreContentSurface(long nativeVrShell);
private native void nativeSetHistoryButtonsEnabled( private native void nativeSetHistoryButtonsEnabled(
long nativeVrShell, boolean canGoBack, boolean canGoForward); long nativeVrShell, boolean canGoBack, boolean canGoForward);
private native void nativeRequestToExitVr(long nativeVrShell, @UiUnsupportedMode int reason);
private native void nativeLogUnsupportedModeUserMetric(
long nativeVrShell, @UiUnsupportedMode int mode);
} }
...@@ -1167,6 +1167,7 @@ chrome_java_sources = [ ...@@ -1167,6 +1167,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java", "java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrFeedbackStatus.java", "java/src/org/chromium/chrome/browser/vr_shell/VrFeedbackStatus.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java", "java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
"java/src/org/chromium/chrome/browser/vr_shell/OnExitVrRequestListener.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrFirstRunActivity.java", "java/src/org/chromium/chrome/browser/vr_shell/VrFirstRunActivity.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrShell.java", "java/src/org/chromium/chrome/browser/vr_shell/VrShell.java",
"java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java", "java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java",
......
...@@ -470,6 +470,12 @@ void VrShell::SetHistoryButtonsEnabled(JNIEnv* env, ...@@ -470,6 +470,12 @@ void VrShell::SetHistoryButtonsEnabled(JNIEnv* env,
ui_->SetHistoryButtonsEnabled(can_go_back, can_go_forward); ui_->SetHistoryButtonsEnabled(can_go_back, can_go_forward);
} }
void VrShell::RequestToExitVr(JNIEnv* env,
const JavaParamRef<jobject>& obj,
int reason) {
ui_->SetExitVrPromptEnabled(true, (vr::UiUnsupportedMode)reason);
}
void VrShell::ContentSurfaceChanged(jobject surface) { void VrShell::ContentSurfaceChanged(jobject surface) {
content_surface_ = surface; content_surface_ = surface;
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
...@@ -585,16 +591,18 @@ void VrShell::ExitFullscreen() { ...@@ -585,16 +591,18 @@ void VrShell::ExitFullscreen() {
} }
} }
void VrShell::ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode, void VrShell::LogUnsupportedModeUserMetric(JNIEnv* env,
bool show_exit_warning) { const JavaParamRef<jobject>& obj,
if (mode == vr::UiUnsupportedMode::kUnhandledPageInfo) { int mode) {
LogUnsupportedModeUserMetric((vr::UiUnsupportedMode)mode);
}
void VrShell::LogUnsupportedModeUserMetric(vr::UiUnsupportedMode mode) {
UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode, UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode,
vr::UiUnsupportedMode::kCount); vr::UiUnsupportedMode::kCount);
JNIEnv* env = base::android::AttachCurrentThread(); }
Java_VrShellImpl_onUnhandledPageInfo(env, j_vr_shell_.obj());
return; void VrShell::ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode) {
}
if (show_exit_warning) {
ui_->SetIsExiting(); ui_->SetIsExiting();
PostToGlThread(FROM_HERE, base::Bind(&VrShellGl::set_is_exiting, PostToGlThread(FROM_HERE, base::Bind(&VrShellGl::set_is_exiting,
gl_thread_->GetVrShellGl(), true)); gl_thread_->GetVrShellGl(), true));
...@@ -602,35 +610,39 @@ void VrShell::ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode, ...@@ -602,35 +610,39 @@ void VrShell::ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode,
FROM_HERE, FROM_HERE,
base::Bind(&VrShell::ForceExitVr, weak_ptr_factory_.GetWeakPtr()), base::Bind(&VrShell::ForceExitVr, weak_ptr_factory_.GetWeakPtr()),
kExitVrDueToUnsupportedModeDelay); kExitVrDueToUnsupportedModeDelay);
} else { LogUnsupportedModeUserMetric(mode);
ForceExitVr();
}
UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode,
vr::UiUnsupportedMode::kCount);
} }
void VrShell::OnUnsupportedMode(vr::UiUnsupportedMode mode) { void VrShell::OnUnsupportedMode(vr::UiUnsupportedMode mode) {
switch (mode) { switch (mode) {
case vr::UiUnsupportedMode::kUnhandledPageInfo: case vr::UiUnsupportedMode::kUnhandledPageInfo: {
ui_->SetExitVrPromptEnabled(true, mode); JNIEnv* env = base::android::AttachCurrentThread();
Java_VrShellImpl_onUnhandledPageInfo(env, j_vr_shell_.obj());
break; break;
}
default: default:
ExitVrDueToUnsupportedMode(mode, true); ExitVrDueToUnsupportedMode(mode);
break; break;
} }
} }
void VrShell::OnExitVrPromptResult(vr::UiUnsupportedMode reason, void VrShell::OnExitVrPromptResult(vr::UiUnsupportedMode reason,
vr::ExitVrPromptChoice choice) { vr::ExitVrPromptChoice choice) {
bool should_exit;
switch (choice) { switch (choice) {
case vr::ExitVrPromptChoice::CHOICE_NONE: case vr::ExitVrPromptChoice::CHOICE_NONE:
case vr::ExitVrPromptChoice::CHOICE_STAY: case vr::ExitVrPromptChoice::CHOICE_STAY:
ui_->SetExitVrPromptEnabled(false, vr::UiUnsupportedMode::kCount); ui_->SetExitVrPromptEnabled(false, vr::UiUnsupportedMode::kCount);
return; should_exit = false;
break;
case vr::ExitVrPromptChoice::CHOICE_EXIT: case vr::ExitVrPromptChoice::CHOICE_EXIT:
ExitVrDueToUnsupportedMode(reason, false); should_exit = true;
return; break;
} }
JNIEnv* env = base::android::AttachCurrentThread();
Java_VrShellImpl_onExitVrRequestResult(env, j_vr_shell_.obj(),
static_cast<int>(reason), should_exit);
} }
void VrShell::UpdateVSyncInterval(base::TimeTicks vsync_timebase, void VrShell::UpdateVSyncInterval(base::TimeTicks vsync_timebase,
......
...@@ -138,6 +138,13 @@ class VrShell : public device::GvrDelegate, ...@@ -138,6 +138,13 @@ class VrShell : public device::GvrDelegate,
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
jboolean can_go_back, jboolean can_go_back,
jboolean can_go_forward); jboolean can_go_forward);
void RequestToExitVr(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
int reason);
void LogUnsupportedModeUserMetric(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
int mode);
void ContentWebContentsDestroyed(); void ContentWebContentsDestroyed();
// Called when our WebContents have been hidden. Usually a sign that something // Called when our WebContents have been hidden. Usually a sign that something
...@@ -173,6 +180,7 @@ class VrShell : public device::GvrDelegate, ...@@ -173,6 +180,7 @@ class VrShell : public device::GvrDelegate,
void ForceExitVr(); void ForceExitVr();
void ExitPresent(); void ExitPresent();
void ExitFullscreen(); void ExitFullscreen();
void LogUnsupportedModeUserMetric(vr::UiUnsupportedMode mode);
void OnUnsupportedMode(vr::UiUnsupportedMode mode); void OnUnsupportedMode(vr::UiUnsupportedMode mode);
void OnExitVrPromptResult(vr::UiUnsupportedMode reason, void OnExitVrPromptResult(vr::UiUnsupportedMode reason,
vr::ExitVrPromptChoice choice); vr::ExitVrPromptChoice choice);
...@@ -215,8 +223,7 @@ class VrShell : public device::GvrDelegate, ...@@ -215,8 +223,7 @@ class VrShell : public device::GvrDelegate,
bool HasDaydreamSupport(JNIEnv* env); bool HasDaydreamSupport(JNIEnv* env);
void ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode, void ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode);
bool show_exit_warning);
bool vr_shell_enabled_; bool vr_shell_enabled_;
......
...@@ -10,6 +10,7 @@ namespace vr { ...@@ -10,6 +10,7 @@ namespace vr {
// Ensure that this stays in sync with VRUnsupportedMode in enums.xml // Ensure that this stays in sync with VRUnsupportedMode in enums.xml
// These values are written to logs. New enum values can be added, but existing // These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused. // enums must never be renumbered or deleted and reused.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.vr_shell
enum class UiUnsupportedMode : int { enum class UiUnsupportedMode : int {
kUnhandledCodePoint = 0, kUnhandledCodePoint = 0,
kCouldNotElideURL, kCouldNotElideURL,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment