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") {
java_cpp_enum("chrome_vr_android_java_enums_srcjar") {
sources = [
"//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 {
* Returns whether the back button is enabled.
*/
Boolean isBackButtonEnabled();
/**
* Requests to exit VR.
*/
void requestToExitVr(@UiUnsupportedMode int reason);
}
......@@ -51,7 +51,6 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
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.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.IntentUtils;
......@@ -157,9 +156,10 @@ public class VrShellDelegate
private Boolean mInVrAtChromeLaunch;
private boolean mShowingDaydreamDoff;
private boolean mDoffOptional;
// Whether we should show the PageInfo UI. This is shown when we force exit the user
// out of VR when they attempt to view the PageInfo.
private boolean mShouldShowPageInfo;
// Listener to be called once we exited VR due to to an unsupported mode, e.g. the user clicked
// the URL bar security icon.
private OnExitVrRequestListener mOnExitVrRequestListener;
private boolean mExitedDueToUnsupportedMode = false;
private boolean mExitingCct;
private boolean mPaused;
private int mRestoreSystemUiVisibilityFlag = -1;
......@@ -383,6 +383,16 @@ public class VrShellDelegate
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
private static VrShellDelegate getInstance() {
Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
......@@ -619,7 +629,6 @@ public class VrShellDelegate
case ActivityState.RESUMED:
if (mInVr && activity != mActivity) {
if (mShowingDaydreamDoff) {
mShouldShowPageInfo = false;
onExitVrResult(true);
} else {
shutdownVr(true /* disableVrMode */, false /* canReenter */,
......@@ -784,7 +793,7 @@ public class VrShellDelegate
mVrDaydreamApi.launchVrHomescreen();
return;
}
mShouldShowPageInfo = false;
mExitedDueToUnsupportedMode = false;
shutdownNonPresentingNativeContext();
// Lock orientation to landscape after enter VR.
......@@ -1045,6 +1054,18 @@ public class VrShellDelegate
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
private boolean exitWebVRPresent() {
if (!mInVr) return false;
......@@ -1206,10 +1227,8 @@ public class VrShellDelegate
if (!mDoffOptional && !success && showDoff(false /* optional */)) return;
mShowingDaydreamDoff = false;
callOnExitVrRequestListener(success);
if (success) {
if (mShouldShowPageInfo) {
sInstance.showPageInfoPopup();
}
shutdownVr(true /* disableVrMode */, false /* canReenter */,
!mExitingCct /* stayingInChrome */);
if (mExitingCct) ((CustomTabActivity) mActivity).finishAndClose(false);
......@@ -1308,10 +1327,18 @@ public class VrShellDelegate
promptForFeedbackIfNeeded(stayingInChrome);
if (stayingInChrome) createNonPresentingNativeContext();
// We don't want to show the PageInfo prompt if we return to Chrome
// after shutting down for reasons other than a successful DOFF (e.g.
// clicking the controller home button and returning to Chrome).
mShouldShowPageInfo = false;
assert mOnExitVrRequestListener == null;
}
private void callOnExitVrRequestListener(boolean success) {
if (mOnExitVrRequestListener != null) {
if (success) {
mOnExitVrRequestListener.onSucceeded();
} else {
mOnExitVrRequestListener.onDenied();
}
}
mOnExitVrRequestListener = null;
}
private void showDoffAndExitVrInternal(boolean optional) {
......@@ -1320,9 +1347,14 @@ public class VrShellDelegate
shutdownVr(true /* disableVrMode */, false /* canReenter */, true /* stayingInChrome */);
}
/* package */ void onUnhandledPageInfo() {
mShouldShowPageInfo = true;
showDoffAndExitVrInternal(true);
/* package */ void onExitVrRequestResult(boolean shouldExit) {
assert mOnExitVrRequestListener != null;
if (shouldExit) {
mExitedDueToUnsupportedMode = true;
showDoffAndExitVrInternal(true);
} else {
callOnExitVrRequestListener(false);
}
}
/* package */ void exitCct() {
......@@ -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) {
// 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?
......@@ -1396,7 +1419,7 @@ public class VrShellDelegate
if (!stayingInChrome) return;
if (VrFeedbackStatus.getFeedbackOptOut()) return;
if (!mVrBrowserUsed) return;
if (mShouldShowPageInfo) return;
if (mExitedDueToUnsupportedMode) return;
int exitCount = VrFeedbackStatus.getUserExitedAndEntered2DCount();
VrFeedbackStatus.setUserExitedAndEntered2DCount((exitCount + 1) % mFeedbackFrequency);
......
......@@ -33,6 +33,7 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.NativePage;
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.InterceptNavigationDelegateImpl;
import org.chromium.chrome.browser.tab.Tab;
......@@ -451,7 +452,16 @@ public class VrShellImpl
// the security icon in the URL bar.
@CalledByNative
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.
......@@ -652,6 +662,19 @@ public class VrShellImpl
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
private void showTab(int id) {
Tab tab = mActivity.getTabModelSelector().getTabById(id);
......@@ -780,4 +803,7 @@ public class VrShellImpl
private native void nativeRestoreContentSurface(long nativeVrShell);
private native void nativeSetHistoryButtonsEnabled(
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 = [
"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/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/VrShell.java",
"java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java",
......
......@@ -470,6 +470,12 @@ void VrShell::SetHistoryButtonsEnabled(JNIEnv* env,
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) {
content_surface_ = surface;
JNIEnv* env = base::android::AttachCurrentThread();
......@@ -585,52 +591,58 @@ void VrShell::ExitFullscreen() {
}
}
void VrShell::ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode,
bool show_exit_warning) {
if (mode == vr::UiUnsupportedMode::kUnhandledPageInfo) {
UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode,
vr::UiUnsupportedMode::kCount);
JNIEnv* env = base::android::AttachCurrentThread();
Java_VrShellImpl_onUnhandledPageInfo(env, j_vr_shell_.obj());
return;
}
if (show_exit_warning) {
ui_->SetIsExiting();
PostToGlThread(FROM_HERE, base::Bind(&VrShellGl::set_is_exiting,
gl_thread_->GetVrShellGl(), true));
main_thread_task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&VrShell::ForceExitVr, weak_ptr_factory_.GetWeakPtr()),
kExitVrDueToUnsupportedModeDelay);
} else {
ForceExitVr();
}
void VrShell::LogUnsupportedModeUserMetric(JNIEnv* env,
const JavaParamRef<jobject>& obj,
int mode) {
LogUnsupportedModeUserMetric((vr::UiUnsupportedMode)mode);
}
void VrShell::LogUnsupportedModeUserMetric(vr::UiUnsupportedMode mode) {
UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode,
vr::UiUnsupportedMode::kCount);
}
void VrShell::ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode) {
ui_->SetIsExiting();
PostToGlThread(FROM_HERE, base::Bind(&VrShellGl::set_is_exiting,
gl_thread_->GetVrShellGl(), true));
main_thread_task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&VrShell::ForceExitVr, weak_ptr_factory_.GetWeakPtr()),
kExitVrDueToUnsupportedModeDelay);
LogUnsupportedModeUserMetric(mode);
}
void VrShell::OnUnsupportedMode(vr::UiUnsupportedMode mode) {
switch (mode) {
case vr::UiUnsupportedMode::kUnhandledPageInfo:
ui_->SetExitVrPromptEnabled(true, mode);
case vr::UiUnsupportedMode::kUnhandledPageInfo: {
JNIEnv* env = base::android::AttachCurrentThread();
Java_VrShellImpl_onUnhandledPageInfo(env, j_vr_shell_.obj());
break;
}
default:
ExitVrDueToUnsupportedMode(mode, true);
ExitVrDueToUnsupportedMode(mode);
break;
}
}
void VrShell::OnExitVrPromptResult(vr::UiUnsupportedMode reason,
vr::ExitVrPromptChoice choice) {
bool should_exit;
switch (choice) {
case vr::ExitVrPromptChoice::CHOICE_NONE:
case vr::ExitVrPromptChoice::CHOICE_STAY:
ui_->SetExitVrPromptEnabled(false, vr::UiUnsupportedMode::kCount);
return;
should_exit = false;
break;
case vr::ExitVrPromptChoice::CHOICE_EXIT:
ExitVrDueToUnsupportedMode(reason, false);
return;
should_exit = true;
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,
......
......@@ -138,6 +138,13 @@ class VrShell : public device::GvrDelegate,
const base::android::JavaParamRef<jobject>& obj,
jboolean can_go_back,
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();
// Called when our WebContents have been hidden. Usually a sign that something
......@@ -173,6 +180,7 @@ class VrShell : public device::GvrDelegate,
void ForceExitVr();
void ExitPresent();
void ExitFullscreen();
void LogUnsupportedModeUserMetric(vr::UiUnsupportedMode mode);
void OnUnsupportedMode(vr::UiUnsupportedMode mode);
void OnExitVrPromptResult(vr::UiUnsupportedMode reason,
vr::ExitVrPromptChoice choice);
......@@ -215,8 +223,7 @@ class VrShell : public device::GvrDelegate,
bool HasDaydreamSupport(JNIEnv* env);
void ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode,
bool show_exit_warning);
void ExitVrDueToUnsupportedMode(vr::UiUnsupportedMode mode);
bool vr_shell_enabled_;
......
......@@ -10,6 +10,7 @@ namespace vr {
// 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
// enums must never be renumbered or deleted and reused.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.vr_shell
enum class UiUnsupportedMode : int {
kUnhandledCodePoint = 0,
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