Commit 476692c6 authored by Gordon Seto's avatar Gordon Seto Committed by Commit Bot

[CrOS Settings] Add flip camera button to Activation Code Page

Add ability for users to switch between cameras when scanning
for QR codes on the Activation Code Page.

Screenshot:
https://screenshot.googleplex.com/jacMJUL3HhZAAr2.png

Bug: 1093185
Change-Id: I32282cd62e0b98c72d4a76c5257b3e6f02f111c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2510740
Commit-Queue: Gordon Seto <gordonseto@google.com>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarAzeem Arshad <azeemarshad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825032}
parent 7c12bdb5
...@@ -318,6 +318,9 @@ ...@@ -318,6 +318,9 @@
<message name="IDS_CELLULAR_SETUP_ESIM_PAGE_USE_CAMERA" desc="Label for button that uses the camera to scan for QR codes when clicked."> <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_USE_CAMERA" desc="Label for button that uses the camera to scan for QR codes when clicked.">
Use camera to scan QR code Use camera to scan QR code
</message> </message>
<message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SWITCH_CAMERA" desc="Label for button that toggles the camera between user-facing and environment-facing.">
Flip camera
</message>
<message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_SUCCESS" desc="Label informing the user that an an activation code was scanned successfully."> <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_SUCCESS" desc="Label informing the user that an an activation code was scanned successfully.">
Activation code detected Activation code detected
</message> </message>
......
b5ef8f6cfcaadaa1e68fe124c3853d3238a7245c
\ No newline at end of file
...@@ -49,6 +49,7 @@ constexpr webui::LocalizedString kLocalizedStringsWithoutPlaceholders[] = { ...@@ -49,6 +49,7 @@ constexpr webui::LocalizedString kLocalizedStringsWithoutPlaceholders[] = {
{"finalPageErrorTitle", IDS_CELLULAR_SETUP_FINAL_PAGE_ERROR_TITLE}, {"finalPageErrorTitle", IDS_CELLULAR_SETUP_FINAL_PAGE_ERROR_TITLE},
{"finalPageErrorMessage", IDS_CELLULAR_SETUP_FINAL_PAGE_ERROR_MESSAGE}, {"finalPageErrorMessage", IDS_CELLULAR_SETUP_FINAL_PAGE_ERROR_MESSAGE},
{"scanQRCode", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE}, {"scanQRCode", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE},
{"switchCamera", IDS_CELLULAR_SETUP_ESIM_PAGE_SWITCH_CAMERA},
{"useCamera", IDS_CELLULAR_SETUP_ESIM_PAGE_USE_CAMERA}, {"useCamera", IDS_CELLULAR_SETUP_ESIM_PAGE_USE_CAMERA},
{"scanQRCodeSuccess", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_SUCCESS}, {"scanQRCodeSuccess", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_SUCCESS},
{"qrCodeRetry", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_RETRY}}; {"qrCodeRetry", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_RETRY}};
......
...@@ -243,6 +243,7 @@ if (include_js_tests) { ...@@ -243,6 +243,7 @@ if (include_js_tests) {
if (is_chromeos) { if (is_chromeos) {
data += [ data += [
"$root_gen_dir/chrome/test/data/webui/chromeos/fake_network_config_mojom.m.js", "$root_gen_dir/chrome/test/data/webui/chromeos/fake_network_config_mojom.m.js",
"$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.m.js",
"$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.m.js", "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/base_page_test.m.js",
"$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.m.js", "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/button_bar_test.m.js",
"$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.m.js", "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/cellular_setup_test.m.js",
......
...@@ -7,6 +7,7 @@ import("//ui/webui/resources/tools/js_modulizer.gni") ...@@ -7,6 +7,7 @@ import("//ui/webui/resources/tools/js_modulizer.gni")
js_modulizer("modulize") { js_modulizer("modulize") {
input_files = [ input_files = [
"activation_code_page_test.js",
"base_page_test.js", "base_page_test.js",
"button_bar_test.js", "button_bar_test.js",
"cellular_setup_test.js", "cellular_setup_test.js",
...@@ -18,10 +19,12 @@ js_modulizer("modulize") { ...@@ -18,10 +19,12 @@ js_modulizer("modulize") {
"sim_detect_page_test.js", "sim_detect_page_test.js",
"fake_cellular_setup_delegate.js", "fake_cellular_setup_delegate.js",
"fake_cellular_setup_remote.js", "fake_cellular_setup_remote.js",
"fake_media_devices.js",
] ]
namespace_rewrites = cr_components_chromeos_namespace_rewrites + [ namespace_rewrites = cr_components_chromeos_namespace_rewrites + [
"cellular_setup.FakeCellularSetupDelegate|FakeCellularSetupDelegate", "cellular_setup.FakeCellularSetupDelegate|FakeCellularSetupDelegate",
"cellular_setup.FakeCarrierPortalHandlerRemote|FakeCarrierPortalHandlerRemote", "cellular_setup.FakeCarrierPortalHandlerRemote|FakeCarrierPortalHandlerRemote",
"cellular_setup.FakeCellularSetupRemote|FakeCellularSetupRemote", "cellular_setup.FakeCellularSetupRemote|FakeCellularSetupRemote",
"cellular_setup.FakeMediaDevices|FakeMediaDevices",
] ]
} }
// Copyright 2020 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.
// clang-format off
// #import 'chrome://os-settings/strings.m.js';
// #import 'chrome://resources/cr_components/chromeos/cellular_setup/activation_code_page.m.js';
// #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
// #import {assertTrue} from '../../../chai_assert.js';
// #import {FakeMediaDevices} from './fake_media_devices.m.js';
// clang-format on
suite('CrComponentsActivationCodePageTest', function() {
let activationCodePage;
/** @type {?FakeMediaDevices} */
let mediaDevices = null;
function flushAsync() {
Polymer.dom.flush();
// Use setTimeout to wait for the next macrotask.
return new Promise(resolve => setTimeout(resolve));
}
setup(function() {
activationCodePage = document.createElement('activation-code-page');
document.body.appendChild(activationCodePage);
Polymer.dom.flush();
mediaDevices = new cellular_setup.FakeMediaDevices();
mediaDevices.addDevice();
activationCodePage.setMediaDevices(mediaDevices);
});
test('Button states', async function() {
const video = activationCodePage.$$('#video');
const startScanningContainer =
activationCodePage.$$('#startScanningContainer');
const startScanningButton = activationCodePage.$$('#startScanningButton');
const scanSuccessContainer = activationCodePage.$$('#scanSuccessContainer');
const switchCameraButton = activationCodePage.$$('#switchCameraButton');
assertTrue(!!video);
assertTrue(!!startScanningContainer);
assertTrue(!!startScanningButton);
assertTrue(!!scanSuccessContainer);
assertTrue(!!switchCameraButton);
// Initial state should only be showing the start scanning UI.
assertFalse(startScanningContainer.hidden);
assertTrue(video.hidden);
assertTrue(scanSuccessContainer.hidden);
assertTrue(switchCameraButton.hidden);
// Click the start scanning button.
startScanningButton.click();
await flushAsync();
// The video should be visible and start scanning UI hidden.
assertFalse(video.hidden);
assertTrue(startScanningContainer.hidden);
assertTrue(scanSuccessContainer.hidden);
assertTrue(switchCameraButton.hidden);
// Mock detecting an activation code.
activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
await flushAsync();
// The scanSuccessContainer should now be visible, video and start scanning
// UI hidden.
assertFalse(scanSuccessContainer.hidden);
assertTrue(startScanningContainer.hidden);
assertTrue(video.hidden);
});
test('Switch camera button states', async function() {
const video = activationCodePage.$$('#video');
const startScanningButton = activationCodePage.$$('#startScanningButton');
const switchCameraButton = activationCodePage.$$('#switchCameraButton');
assertTrue(!!video);
assertTrue(!!startScanningButton);
assertTrue(!!switchCameraButton);
// Initial state should only be showing the start scanning UI.
assertTrue(video.hidden);
assertTrue(switchCameraButton.hidden);
// Click the start scanning button.
startScanningButton.click();
await flushAsync();
// The video should be visible and switch camera button hidden.
assertFalse(video.hidden);
assertTrue(switchCameraButton.hidden);
assertTrue(mediaDevices.isStreamingUserFacingCamera);
// Add a new video device.
mediaDevices.addDevice();
await flushAsync();
// The switch camera button should now be visible.
assertFalse(switchCameraButton.hidden);
assertTrue(mediaDevices.isStreamingUserFacingCamera);
switchCameraButton.click();
await flushAsync();
// The second device should now be streaming.
assertFalse(mediaDevices.isStreamingUserFacingCamera);
assertFalse(switchCameraButton.hidden);
// Switch back.
switchCameraButton.click();
await flushAsync();
// The first device should be streaming again.
assertTrue(mediaDevices.isStreamingUserFacingCamera);
assertFalse(switchCameraButton.hidden);
// Switch to the second device again.
switchCameraButton.click();
await flushAsync();
assertFalse(mediaDevices.isStreamingUserFacingCamera);
assertFalse(switchCameraButton.hidden);
// Disconnect the second device.
mediaDevices.removeDevice();
await flushAsync();
// The first device should now be streaming and the switch camera button
// hidden.
assertTrue(mediaDevices.isStreamingUserFacingCamera);
assertTrue(switchCameraButton.hidden);
// Mock detecting an activation code.
activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
Polymer.dom.flush();
assertTrue(video.hidden);
});
});
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
suite('CrComponentsEsimFlowUiTest', function() { suite('CrComponentsEsimFlowUiTest', function() {
let eSimPage; let eSimPage;
setup(function() { setup(function() {
eSimPage = document.createElement('esim-flow-ui'); eSimPage = document.createElement('esim-flow-ui');
eSimPage.delegate = new cellular_setup.FakeCellularSetupDelegate(); eSimPage.delegate = new cellular_setup.FakeCellularSetupDelegate();
...@@ -49,7 +50,7 @@ suite('CrComponentsEsimFlowUiTest', function() { ...@@ -49,7 +50,7 @@ suite('CrComponentsEsimFlowUiTest', function() {
cellularSetup.ButtonState.SHOWN_BUT_DISABLED); cellularSetup.ButtonState.SHOWN_BUT_DISABLED);
const activationCodePage = eSimPage.$$('#activationCodePage'); const activationCodePage = eSimPage.$$('#activationCodePage');
activationCodePage.activationCode_ = 'ACTIVATION CODE'; activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
Polymer.dom.flush(); Polymer.dom.flush();
assertTrue( assertTrue(
......
// Copyright 2020 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.
cr.define('cellular_setup', function() {
/**
* @implements {MediaDevices}
*/
/* #export */ class FakeMediaDevices {
constructor() {
this.isStreamingUserFacingCamera = true;
this.devices_ = [];
}
/** override */
addEventListener(type, listener) {
this.deviceChangeListener_ = listener;
}
/** override */
enumerateDevices() {
return new Promise((res, rej) => {
res(this.devices_);
});
}
/** override */
getSupportedConstraints() {
return null;
}
/** override */
getDisplayMedia() {
return new Promise((res, rej) => {
res(null);
});
}
/** override */
getUserMedia(constraints) {
this.isStreamingUserFacingCamera =
constraints.video.facingMode === 'user';
return new Promise((res, rej) => {
res(null);
});
}
/**
* Adds a video input device to the list of media devices.
*/
addDevice() {
let device = {
deviceId: '',
kind: 'videoinput',
label: '',
groupId: '',
};
device.__proto__ = MediaDeviceInfo.prototype;
this.devices_.push(device);
if (this.deviceChangeListener_) {
this.deviceChangeListener_();
}
}
/**
* Removes the most recently added media device from the list of media
* devices.
*/
removeDevice() {
this.devices_.pop();
if (this.devices_.length <= 1) {
this.isStreamingUserFacingCamera = true;
}
if (this.deviceChangeListener_) {
this.deviceChangeListener_();
}
}
}
// #cr_define_end
return {
FakeMediaDevices: FakeMediaDevices,
};
});
\ No newline at end of file
...@@ -54,6 +54,9 @@ GEN('#include "content/public/test/browser_test.h"'); ...@@ -54,6 +54,9 @@ GEN('#include "content/public/test/browser_test.h"');
].forEach(test => registerTest('NetworkComponents', 'network', ...test)); ].forEach(test => registerTest('NetworkComponents', 'network', ...test));
[ [
['ActivationCodePage', 'cellular_setup/activation_code_page_test.js',[
'./cellular_setup/fake_media_devices.js',
]],
['BasePage', 'cellular_setup/base_page_test.js', []], ['BasePage', 'cellular_setup/base_page_test.js', []],
['ButtonBar', 'cellular_setup/button_bar_test.js',[]], ['ButtonBar', 'cellular_setup/button_bar_test.js',[]],
['CellularSetup', 'cellular_setup/cellular_setup_test.js', [ ['CellularSetup', 'cellular_setup/cellular_setup_test.js', [
......
...@@ -32,7 +32,8 @@ GEN('#include "chromeos/constants/chromeos_features.h"'); ...@@ -32,7 +32,8 @@ GEN('#include "chromeos/constants/chromeos_features.h"');
['NetworkSiminfo', 'network/network_siminfo_test.m.js'], ['NetworkSiminfo', 'network/network_siminfo_test.m.js'],
].forEach(test => registerTest('NetworkComponents', ...test)); ].forEach(test => registerTest('NetworkComponents', ...test));
[['BasePage', 'cellular_setup/base_page_test.m.js'], [['ActivationCodePage', 'cellular_setup/activation_code_page_test.m.js'],
['BasePage', 'cellular_setup/base_page_test.m.js'],
['ButtonBar', 'cellular_setup/button_bar_test.m.js'], ['ButtonBar', 'cellular_setup/button_bar_test.m.js'],
['CellularSetup', 'cellular_setup/cellular_setup_test.m.js'], ['CellularSetup', 'cellular_setup/cellular_setup_test.m.js'],
['EsimFlowUi', 'cellular_setup/esim_flow_ui_test.m.js'], ['EsimFlowUi', 'cellular_setup/esim_flow_ui_test.m.js'],
......
<link rel="import" href="../../../html/polymer.html"> <link rel="import" href="../../../html/polymer.html">
<link rel="import" href="../../../html/i18n_behavior.html"> <link rel="import" href="../../../html/i18n_behavior.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
<link rel="import" href="base_page.html"> <link rel="import" href="base_page.html">
<link rel="import" href="cellular_setup_delegate.html"> <link rel="import" href="cellular_setup_delegate.html">
...@@ -54,8 +55,10 @@ ...@@ -54,8 +55,10 @@
font-weight: 500; font-weight: 500;
} }
#startScanningImage { .button-image {
height: 20px;
margin-inline-end: 8px; margin-inline-end: 8px;
width: 20px;
} }
#useCameraAgainButton { #useCameraAgainButton {
...@@ -66,38 +69,66 @@ ...@@ -66,38 +69,66 @@
} }
#scanSuccessImage { #scanSuccessImage {
height: 20px;
position: absolute; position: absolute;
width: 20px;
} }
#scanSuccessMessage { #scanSuccessMessage {
padding-inline-end: 0; padding-inline-end: 0;
padding-inline-start: 30px; padding-inline-start: 30px;
} }
#switchCameraButton {
background-color: rgba(0, 0, 0, 0.04);
border-radius: 4px;
color: var(--google-grey-refresh-300);
margin: 8px;
padding: 8px;
position: absolute;
right: 0;
z-index: 2;
}
#switchCameraButton img {
filter: brightness(2.1);
}
</style> </style>
<base-page> <base-page>
<div slot="page-body"> <div slot="page-body">
[[i18n('scanQRCode')]] <span id="scanQrCodeDescription">
<!-- TODO(crbug.com/1093185): Add flip camera button --> [[i18n('scanQRCode')]]
</span>
<div id="esimQrCodeDetection"> <div id="esimQrCodeDetection">
<cr-button id="switchCameraButton"
on-click="onSwitchCameraButtonPressed_"
hidden$="[[isUiElementHidden_(UiElement.SWITCH_CAMERA, state_, hasMultipleCameras_)]]"
disabled="[[isUiElementDisabled_(UiElement.SWITCH_CAMERA, state_)]]">
<img class="button-image"
src="activation_code_page_switch_camera.svg"
aria-hidden="true">
[[i18n('switchCamera')]]
</cr-button>
<video id="video"
hidden$="[[isUiElementHidden_(UiElement.VIDEO, state_)]]">
</video>
<div class="center" id="startScanningContainer" <div class="center" id="startScanningContainer"
hidden$="[[!isInitialState_]]"> hidden$="[[isUiElementHidden_(UiElement.START_SCANNING, state_)]]">
<cr-button class="label" <cr-button class="label"
id="startScanningButton" id="startScanningButton"
on-click="startScanning_"> on-click="startScanning_"
<img class="label-image" aria-describedby="scanQrCodeDescription">
id="startScanningImage" <img class="button-image"
src="activation_code_page_camera.svg" src="activation_code_page_camera.svg"
width="20" height="20"
aria-hidden="true"> aria-hidden="true">
[[i18n('useCamera')]] [[i18n('useCamera')]]
</cr-button> </cr-button>
</div> </div>
<div class="center" id="scanSuccessContainer" <div class="center" id="scanSuccessContainer"
hidden$="[[!activationCode_]]"> hidden$="[[isUiElementHidden_(UiElement.SCAN_SUCCESS, state_)]]">
<div> <div>
<img id="scanSuccessImage" <img id="scanSuccessImage"
src="activation_code_page_checked.svg" src="activation_code_page_checked.svg"
width="20" height="20"
aria-hidden="true"> aria-hidden="true">
<span class="label" id="scanSuccessMessage"> <span class="label" id="scanSuccessMessage">
[[i18n('scanQRCodeSuccess')]] [[i18n('scanQRCodeSuccess')]]
...@@ -108,7 +139,6 @@ ...@@ -108,7 +139,6 @@
[[i18n('qrCodeRetry')]] [[i18n('qrCodeRetry')]]
</cr-button> </cr-button>
</div> </div>
<video id="video" hidden$="[[!qrCodeScanInProgress_]]"></video>
</div> </div>
<cr-input id="activationCode" <cr-input id="activationCode"
label="[[i18n('activationCode')]]" label="[[i18n('activationCode')]]"
......
...@@ -8,6 +8,24 @@ ...@@ -8,6 +8,24 @@
*/ */
const QR_CODE_DETECTION_INTERVAL_MS = 1000; const QR_CODE_DETECTION_INTERVAL_MS = 1000;
/** @enum {number} */
const PageState = {
INITIAL: 1,
SCANNING_USER_FACING: 2,
SCANNING_ENVIRONMENT_FACING: 3,
SWITCHING_CAM_USER_TO_ENVIRONMENT: 4,
SWITCHING_CAM_ENVIRONMENT_TO_USER: 5,
SUCCESS: 6,
};
/** @enum {number} */
const UiElement = {
START_SCANNING: 1,
VIDEO: 2,
SCAN_SUCCESS: 3,
SWITCH_CAMERA: 4,
};
/** /**
* Page in eSIM Setup flow that accepts activation code. User has option for * Page in eSIM Setup flow that accepts activation code. User has option for
* manual entry or scan a QR code. * manual entry or scan a QR code.
...@@ -25,19 +43,41 @@ Polymer({ ...@@ -25,19 +43,41 @@ Polymer({
observer: 'onActivationCodeChanged_', observer: 'onActivationCodeChanged_',
}, },
/**
* @type {!PageState}
* @private
*/
state_: {
type: Object,
value: PageState,
},
/** @private */ /** @private */
qrCodeScanInProgress_: { hasMultipleCameras_: {
type: Boolean, type: Boolean,
value: false, value: false,
observer: 'onHasMultipleCamerasChanged_',
}, },
/** @private */ /**
isInitialState_: { * Enum used as an ID for specific UI elements.
type: Boolean, * A UiElement is passed between html and JS for
value: true, * certain UI elements to determine their state.
*
* @type {!UiElement}
*/
UiElement: {
type: Object,
value: UiElement,
}, },
}, },
/**
* @type {MediaDevices}
* @private
*/
mediaDevices_: null,
/** /**
* @type {?MediaStream} * @type {?MediaStream}
* @private * @private
...@@ -50,7 +90,13 @@ Polymer({ ...@@ -50,7 +90,13 @@ Polymer({
*/ */
qrCodeDetectorTimer_: null, qrCodeDetectorTimer_: null,
/** override */ /** @override */
ready() {
this.setMediaDevices(navigator.mediaDevices);
this.state_ = PageState.INITIAL;
},
/** @override */
detached() { detached() {
if (this.stream_) { if (this.stream_) {
this.stream_.getTracks()[0].stop(); this.stream_.getTracks()[0].stop();
...@@ -58,23 +104,72 @@ Polymer({ ...@@ -58,23 +104,72 @@ Polymer({
if (this.qrCodeDetectorTimer_) { if (this.qrCodeDetectorTimer_) {
clearTimeout(this.qrCodeDetectorTimer_); clearTimeout(this.qrCodeDetectorTimer_);
} }
this.mediaDevices_.removeEventListener(
'devicechange', this.updateHasMultipleCameras_.bind(this));
},
/**
* @param {MediaDevices} mediaDevices
*/
setMediaDevices(mediaDevices) {
this.mediaDevices_ = mediaDevices;
this.mediaDevices_.addEventListener(
'devicechange', this.updateHasMultipleCameras_.bind(this));
},
/** @private */
updateHasMultipleCameras_() {
this.mediaDevices_.enumerateDevices().then(devices => {
const numVideoInputDevices =
devices.filter(device => device.kind === 'videoinput').length;
this.hasMultipleCameras_ = numVideoInputDevices > 1;
});
},
/** @private */
onHasMultipleCamerasChanged_() {
// If the user was using an environment-facing camera and it was removed,
// restart scanning with the user-facing camera.
if ((this.state_ === PageState.SCANNING_ENVIRONMENT_FACING) &&
!this.hasMultipleCameras_) {
this.state_ = PageState.SWITCHING_CAM_ENVIRONMENT_TO_USER;
this.startScanning_();
}
}, },
/** private */ /** private */
startScanning_() { startScanning_() {
// TODO(crbug.com/1093185): Add logic for changing stream if user flips const oldStream = this.stream_;
// camera. Add error handling for camera not working. if (this.qrCodeDetectorTimer_) {
navigator.mediaDevices clearTimeout(this.qrCodeDetectorTimer_);
.getUserMedia({video: {height: 130, width: 482}, audio: false}) }
const useUserFacingCamera =
this.state_ !== PageState.SWITCHING_CAM_USER_TO_ENVIRONMENT;
this.mediaDevices_
.getUserMedia({
video: {
height: 130,
width: 482,
facingMode: useUserFacingCamera ? 'user' : 'environment'
},
audio: false
})
.then(stream => { .then(stream => {
this.stream_ = stream; this.stream_ = stream;
const video = this.$.video; if (stream) {
video.srcObject = stream; const video = this.$.video;
video.play(); video.srcObject = stream;
video.play();
}
if (oldStream) {
oldStream.getTracks()[0].stop();
}
this.activationCode_ = ''; this.activationCode_ = '';
this.qrCodeScanInProgress_ = true; this.state_ = useUserFacingCamera ?
this.isInitialState_ = false; PageState.SCANNING_USER_FACING :
PageState.SCANNING_ENVIRONMENT_FACING;
this.detectQrCode_(stream); this.detectQrCode_(stream);
}); });
...@@ -97,7 +192,6 @@ Polymer({ ...@@ -97,7 +192,6 @@ Polymer({
if (activationCode) { if (activationCode) {
clearTimeout(this.qrCodeDetectorTimer_); clearTimeout(this.qrCodeDetectorTimer_);
this.activationCode_ = activationCode; this.activationCode_ = activationCode;
this.qrCodeScanInProgress_ = false;
} }
}).bind(this), }).bind(this),
QR_CODE_DETECTION_INTERVAL_MS); QR_CODE_DETECTION_INTERVAL_MS);
...@@ -137,6 +231,7 @@ Polymer({ ...@@ -137,6 +231,7 @@ Polymer({
if (this.stream_) { if (this.stream_) {
this.stream_.getTracks()[0].stop(); this.stream_.getTracks()[0].stop();
} }
this.state_ = PageState.SUCCESS;
} }
}, },
...@@ -155,4 +250,51 @@ Polymer({ ...@@ -155,4 +250,51 @@ Polymer({
} }
return null; return null;
}, },
/** @private */
onSwitchCameraButtonPressed_() {
if (this.state_ === PageState.SCANNING_USER_FACING) {
this.state_ = PageState.SWITCHING_CAM_USER_TO_ENVIRONMENT;
} else if (this.state_ === PageState.SCANNING_ENVIRONMENT_FACING) {
this.state_ = PageState.SWITCHING_CAM_ENVIRONMENT_TO_USER;
}
this.startScanning_();
},
/**
* @param {UiElement} uiElement
* @param {PageState} state
* @param {boolean} hasMultipleCameras
* @private
*/
isUiElementHidden_(uiElement, state, hasMultipleCameras) {
switch (uiElement) {
case UiElement.START_SCANNING:
return state !== PageState.INITIAL;
case UiElement.VIDEO:
return state !== PageState.SCANNING_USER_FACING &&
state !== PageState.SCANNING_ENVIRONMENT_FACING;
case UiElement.SCAN_SUCCESS:
return state !== PageState.SUCCESS;
case UiElement.SWITCH_CAMERA:
const isScanning = state === PageState.SCANNING_USER_FACING ||
state === PageState.SCANNING_ENVIRONMENT_FACING;
return !(isScanning && hasMultipleCameras);
}
},
/**
* @param {UiElement} uiElement
* @param {PageState} state
* @private
*/
isUiElementDisabled_(uiElement, state) {
switch (uiElement) {
case UiElement.SWITCH_CAMERA:
return state === PageState.SWITCHING_CAM_USER_TO_ENVIRONMENT ||
state === PageState.SWITCHING_CAM_ENVIRONMENT_TO_USER;
default:
return false;
}
},
}); });
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 9V7H4.802A5.996 5.996 0 0 1 10 4a6 6 0 0 1 5.917 5h2.021c-.491-3.945-3.853-7-7.93-7a7.992 7.992 0 0 0-6.009 2.712L4 3H2v6h5zm5.938 2v2h2.198a5.996 5.996 0 0 1-5.198 3 6 6 0 0 1-5.917-5H2c.492 3.945 3.853 7 7.93 7a7.992 7.992 0 0 0 6.009-2.712V17h2v-6h-5zM10 12a2 2 0 1 0 0-4 2 2 0 0 0 0 4z" fill="#5F6368"/></svg>
\ No newline at end of file
<link rel="import" href="../../../html/polymer.html"> <link rel="import" href="../../../html/polymer.html">
<link rel="import" href="../../../html/i18n_behavior.html"> <link rel="import" href="../../../html/i18n_behavior.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
<link rel="import" href="subflow_behavior.html"> <link rel="import" href="subflow_behavior.html">
<link rel="import" href="cellular_types.html"> <link rel="import" href="cellular_types.html">
<link rel="import" href="cellular_setup_delegate.html"> <link rel="import" href="cellular_setup_delegate.html">
......
...@@ -35,6 +35,10 @@ ...@@ -35,6 +35,10 @@
file="cr_components/chromeos/cellular_setup/activation_code_page_checked.svg" file="cr_components/chromeos/cellular_setup/activation_code_page_checked.svg"
type="BINDATA" type="BINDATA"
compress="gzip" /> compress="gzip" />
<include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_ACTIVATION_CODE_PAGE_SWITCH_CAMERA_SVG"
file="cr_components/chromeos/cellular_setup/activation_code_page_switch_camera.svg"
type="BINDATA"
compress="gzip" />
<include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_ERROR_1X_PNG" <include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_ERROR_1X_PNG"
file="cr_components/chromeos/cellular_setup/error_1x.png" file="cr_components/chromeos/cellular_setup/error_1x.png"
type="BINDATA" type="BINDATA"
......
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