Commit 339c9a3a authored by Jesse Schettler's avatar Jesse Schettler Committed by Chromium LUCI CQ

scanning: Update done page to address UI review

Update the done page to show the app title instead of replacing it. Also
add text to indicate scanning is complete and a link to the scanned
file(s).

Before and after: http://shortn/_U80YRweWzm

Bug: 1059779
Change-Id: I1bb500937677854a4c035b7f0dc9ab27156239f7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2625231Reviewed-by: default avatarZentaro Kavanagh <zentaro@chromium.org>
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843263}
parent b603560d
......@@ -36,24 +36,43 @@ export function scanDoneSectionTest() {
});
test('initializeScanDoneSection', () => {
assertTrue(!!scanDoneSection.$.title);
assertTrue(!!scanDoneSection.$$('#doneButtonContainer'));
});
test('pageNumberUpdatesTitleText', () => {
scanDoneSection.pageNumber = 1;
test('numFilesSavedUpdatesFileSavedText', () => {
scanDoneSection.selectedFolder = 'My files';
scanDoneSection.numFilesSaved = 1;
return flushTasks()
.then(() => {
assertEquals(
'Scanned file saved!',
scanDoneSection.$.title.textContent.trim());
scanDoneSection.pageNumber = 2;
'Your file has been successfully scanned and saved to My files.',
scanDoneSection.$$('#fileSavedText').textContent.trim());
scanDoneSection.numFilesSaved = 2;
return flushTasks();
})
.then(() => {
assertEquals(
'Scanned files saved!',
scanDoneSection.$.title.textContent.trim());
'Your files have been successfully scanned and saved to My ' +
'files.',
scanDoneSection.$$('#fileSavedText').textContent.trim());
});
});
test('selectedFolderUpdatesFileSavedText', () => {
scanDoneSection.selectedFolder = 'Downloads';
scanDoneSection.numFilesSaved = 1;
return flushTasks()
.then(() => {
assertEquals(
'Your file has been successfully scanned and saved to Downloads.',
scanDoneSection.$$('#fileSavedText').textContent.trim());
scanDoneSection.selectedFolder = 'My Drive';
return flushTasks();
})
.then(() => {
assertEquals(
'Your file has been successfully scanned and saved to My Drive.',
scanDoneSection.$$('#fileSavedText').textContent.trim());
});
});
......@@ -66,10 +85,14 @@ export function scanDoneSectionTest() {
const lastScannedFilePath = {'path': '/test/path/scan.jpg'};
scanningBrowserProxy.setPathToFile(lastScannedFilePath.path);
scanDoneSection.lastScannedFilePath = lastScannedFilePath;
scanDoneSection.$$('#showFileButton').click();
scanDoneSection.numFilesSaved = 1;
return flushTasks().then(() => {
assertEquals(1, scanningBrowserProxy.getCallCount('showFileInLocation'));
assertFalse(fileNotFoundEventFired);
scanDoneSection.$$('#folderLink').click();
return flushTasks().then(() => {
assertEquals(
1, scanningBrowserProxy.getCallCount('showFileInLocation'));
assertFalse(fileNotFoundEventFired);
});
});
});
......@@ -81,10 +104,14 @@ export function scanDoneSectionTest() {
scanningBrowserProxy.setPathToFile('/wrong/path/file/so/not/found.jpg');
scanDoneSection.lastScannedFilePath = {'path': '/test/path/scan.jpg'};
scanDoneSection.$$('#showFileButton').click();
scanDoneSection.numFilesSaved = 1;
return flushTasks().then(() => {
assertEquals(1, scanningBrowserProxy.getCallCount('showFileInLocation'));
assertTrue(fileNotFoundEventFired);
scanDoneSection.$$('#folderLink').click();
return flushTasks().then(() => {
assertEquals(
1, scanningBrowserProxy.getCallCount('showFileInLocation'));
assertTrue(fileNotFoundEventFired);
});
});
});
}
......@@ -62,6 +62,7 @@ export function scanToSelectTest() {
/** @type {!HTMLSelectElement} */ (scanToSelect.$$('select'));
return changeSelect(select, /* value */ null, /* selectedIndex */ 1)
.then(() => {
assertEquals(myDownloads, scanToSelect.selectedFolder);
assertEquals(myDownloadsPath, scanToSelect.selectedFilePath);
assertEquals(
myDownloads,
......@@ -73,6 +74,7 @@ export function scanToSelectTest() {
return changeSelect(select, /* value */ null, /* selectedIndex */ 1);
})
.then(() => {
assertEquals(googleDrive, scanToSelect.selectedFolder);
assertEquals(googleDrivePath, scanToSelect.selectedFilePath);
assertEquals(
googleDrive,
......@@ -94,6 +96,7 @@ export function scanToSelectTest() {
/** @type {!HTMLSelectElement} */ (scanToSelect.$$('select'));
return changeSelect(select, /* value */ null, /* selectedIndex */ 1)
.then(() => {
assertEquals(myDownloads, scanToSelect.selectedFolder);
assertEquals(myDownloadsPath, scanToSelect.selectedFilePath);
assertEquals(
myDownloads,
......@@ -105,6 +108,7 @@ export function scanToSelectTest() {
return changeSelect(select, /* value */ null, /* selectedIndex */ 1);
})
.then(() => {
assertEquals(myDownloads, scanToSelect.selectedFolder);
assertEquals(myDownloadsPath, scanToSelect.selectedFilePath);
assertEquals(
myDownloads,
......
......@@ -719,11 +719,11 @@ export function scanningAppTest() {
test('PanelContainerContent', () => {
return initializeScanningApp(expectedScanners, capabilities).then(() => {
const panelContainer = scanningApp.$$('.panel-container');
const panelContainer = scanningApp.$$('#panelContainer');
assertTrue(!!panelContainer);
const leftPanel = scanningApp.$$('.panel-container > .left-panel');
const rightPanel = scanningApp.$$('.panel-container > .right-panel');
const leftPanel = scanningApp.$$('#panelContainer > #leftPanel');
const rightPanel = scanningApp.$$('#panelContainer > #rightPanel');
assertTrue(!!leftPanel);
assertTrue(!!rightPanel);
......
......@@ -54,7 +54,11 @@ export class TestScanningBrowserProxy extends TestBrowserProxy {
getPluralString(name, count) {
this.methodCalled('getPluralString');
return Promise.resolve(
count === 1 ? 'Scanned file saved!' : 'Scanned files saved!');
count === 1 ?
'Your file has been successfully scanned and saved to ' +
'<a id="folderLink">$1</a>.' :
'Your files have been successfully scanned and saved to ' +
'<a id="folderLink">$1</a>.');
}
/** @param {!SelectedPath} selectedPath */
......
......@@ -568,8 +568,8 @@ Try tapping the mic to ask me anything.
</message>
<message name="IDS_SCANNING_APP_FILE_SAVED_TEXT" desc="The text displayed in the Scanning App when a scan job is completed and the file is saved.">
{COUNT, plural,
=1 {Scanned file saved}
other {Scanned files saved}}
=1 {Your file has been successfully scanned and saved to <ph name="LINK_BEGIN">&lt;a id="folderLink"&gt;</ph><ph name="FOLDER_NAME">$1<ex>My files</ex></ph><ph name="LINK_END">&lt;/a&gt;</ph>.}
other {Your files have been successfully scanned and saved to <ph name="LINK_BEGIN">&lt;a id="folderLink"&gt;</ph><ph name="FOLDER_NAME">$1<ex>My files</ex></ph><ph name="LINK_END">&lt;/a&gt;</ph>.}}
</message>
<message name="IDS_SCANNING_APP_DONE_BUTTON_TEXT" desc="The text displayed for the button the user clicks to return to the intial Scanning App page after a scan job is completed.">
Done
......@@ -577,9 +577,6 @@ Try tapping the mic to ask me anything.
<message name="IDS_SCANNING_APP_CANCEL_BUTTON_TEXT" desc="The text displayed for the button to cancel an ongoing scan job.">
Cancel
</message>
<message name="IDS_SCANNING_APP_SHOW_FILE_LOCATION_LABEL" desc="The label for the button that opens the Files app. The Files app will open with the user's saved scan file highlighted.">
Show file location
</message>
<message name="IDS_SCANNING_APP_START_SCAN_FAILED_TOAST" desc="The error message displayed when a scan job fails to start.">
Couldn't start scanning
</message>
......
337906cc5e8ced8334d91f9f5eb5c1e898ed5fe6
\ No newline at end of file
9a31507a9cc0cf2a1777d4f295e4b1514c23216f
\ No newline at end of file
045d413eba40b73fd7ee46e1db668e9900879f64
\ No newline at end of file
......@@ -28,12 +28,6 @@ js_type_check("closure_compile_module") {
]
}
js_library("icons") {
deps = [
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
}
js_library("color_mode_select") {
deps = [
":scanning_app_util",
......@@ -75,10 +69,10 @@ js_library("resolution_select") {
js_library("scan_done_section") {
deps = [
":icons",
":scanning_app_types",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
"//ui/webui/resources/js:i18n_behavior.m",
"//ui/webui/resources/js:load_time_data.m",
]
}
......@@ -168,7 +162,6 @@ html_to_js("web_components") {
js_files = [
"color_mode_select.js",
"file_type_select.js",
"icons.js",
"page_size_select.js",
"resolution_select.js",
"scan_preview.js",
......
<iron-iconset-svg name="scanning">
<svg>
<defs>
<g id="check-mark" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" width="20" height="20" viewBox="0 0 20 20">
<g transform="translate(2.000000, 2.000000)">
<path fill="#1A73E8" d="M8,16 C12.4183,16 16,12.4183 16,8 C16,3.58172 12.4183,0 8,0 C3.58172,0 0,3.58172 0,8 C0,12.4183 3.58172,16 8,16 Z M8,14 C11.3137,14 14,11.3137 14,8 C14,4.68629 11.3137,2 8,2 C4.68629,2 2,4.68629 2,8 C2,11.3137 4.68629,14 8,14 Z"></path>
<polygon fill="#1A73E8" fill-rule="nonzero" points="11.3310353 4.75670585 12.6689647 6.24329415 6.96376255 11.3779761 3.79289322 8.20710678 5.20710678 6.79289322 7.036 8.621"></polygon>
</g>
</g>
</defs>
</svg>
</iron-iconset-svg>
// 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.
import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
const template = html`{__html_template__}`;
document.head.appendChild(template.content);
<style include="scanning-fonts">
#titleContainer {
align-items: center;
display: flex;
margin-bottom: 24px;
margin-inline-start: 32px;
margin-top: 0;
}
.container,
#doneButtonContainer,
#titleContainer {
#doneButtonContainer {
width: 272px;
}
......@@ -17,22 +8,15 @@
@apply --scanning-done-section-option-font;
align-items: center;
color: var(--scanning-done-section-option-text-color);
cursor: pointer;
display: flex;
height: 36px;
margin-bottom: 8px;
margin-inline-end: 32px;
margin-inline-start: 32px;
margin-top: 0;
margin-top: 8px;
padding-inline-end: 16px;
}
#checkMarkIcon {
margin-inline-end: 12px;
min-height: 24px;
min-width: 24px;
}
#doneButtonContainer {
display: flex;
justify-content: flex-end;
......@@ -43,39 +27,21 @@
padding-inline-end: 16px;
}
#title {
@apply --scanning-done-section-title-font;
color: var(--scanning-done-section-title-text-color);
flex-direction: column;
margin: 0;
}
.button {
flex-direction: column;
height: 20px;
margin: 0;
width: 20px;
}
.label {
flex-basis: 252px;
flex-grow: 0;
flex-shrink: 0;
}
#folderLink {
color: var(--google-blue-600);
text-decoration: none;
}
</style>
<div id="titleContainer">
<iron-icon id="checkMarkIcon" icon="scanning:check-mark"></iron-icon>
<h1 id="title">
[[titleText_]]
</h1>
</div>
<div class="container" on-click="showFileInLocation_">
<span id="showFileLocationLabel" class="label" aria-hidden="true">
[[i18n('showFileLocationLabel')]]
<div class="container">
<span id="fileSavedText" class="label"
inner-h-t-m-l="[[fileSavedTextContent_]]">
</span>
<cr-icon-button id="showFileButton" class="button" iron-icon="cr:open-in-new"
aria-labelledby="showFileLocationLabel">
</cr-icon-button>
</div>
<div id="doneButtonContainer">
<cr-button id="doneButton" class="action-button"
......
......@@ -6,9 +6,9 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import './file_path.mojom-lite.js';
import './icons.js';
import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {AppState} from './scanning_app_types.js';
......@@ -30,18 +30,20 @@ Polymer({
properties: {
/** @type {number} */
pageNumber: {
type: Number,
observer: 'onPageNumberChange_',
},
numFilesSaved: Number,
/** @type {?mojoBase.mojom.FilePath} */
lastScannedFilePath: Object,
/** @type {string} */
selectedFolder: String,
/** @private {string} */
titleText_: String,
fileSavedTextContent_: String,
},
observers: ['setFileSavedTextContent_(numFilesSaved, selectedFolder)'],
/** @override */
created() {
// ScanningBrowserProxy is initialized when scanning_app.js is created.
......@@ -65,10 +67,59 @@ Polymer({
},
/** @private */
onPageNumberChange_() {
this.browserProxy_.getPluralString('fileSavedText', this.pageNumber)
setFileSavedTextContent_() {
this.browserProxy_.getPluralString('fileSavedText', this.numFilesSaved)
.then(
/* @type {string} */ (pluralString) => this.titleText_ =
pluralString);
/* @type {string} */ (pluralString) => {
this.fileSavedTextContent_ =
this.getAriaLabelledContent_(loadTimeData.substituteString(
pluralString, this.selectedFolder));
const linkElement = this.$$('#folderLink');
linkElement.setAttribute('href', '#');
linkElement.addEventListener(
'click', () => this.showFileInLocation_());
});
},
/**
* Takes a localized string that contains exactly one anchor tag and labels
* the string contained within the anchor tag with the entire localized
* string. The string should not be bound by element tags. The string should
* not contain any elements other than the single anchor tagged element that
* will be aria-labelledby the entire string.
* @param {string} localizedString
* @return {string}
* @private
*/
getAriaLabelledContent_(localizedString) {
const tempEl = document.createElement('div');
tempEl.innerHTML = localizedString;
const ariaLabelledByIds = [];
tempEl.childNodes.forEach((node, index) => {
// Text nodes should be aria-hidden and associated with an element id
// that the anchor element can be aria-labelledby.
if (node.nodeType == Node.TEXT_NODE) {
const spanNode = document.createElement('span');
spanNode.textContent = node.textContent;
spanNode.id = `id${index}`;
ariaLabelledByIds.push(spanNode.id);
spanNode.setAttribute('aria-hidden', true);
node.replaceWith(spanNode);
return;
}
// The single element node with anchor tags should also be aria-labelledby
// itself in-order with respect to the entire string.
if (node.nodeType == Node.ELEMENT_NODE && node.nodeName == 'A') {
ariaLabelledByIds.push(node.id);
return;
}
});
const anchorTags = tempEl.getElementsByTagName('a');
anchorTags[0].setAttribute('aria-labelledby', ariaLabelledByIds.join(' '));
return tempEl.innerHTML;
},
});
......@@ -7,7 +7,7 @@
<select id="scanToSelect" class="md-select" disabled="[[disabled]]"
on-change="onSelectFolder_" aria-labelledby="scanToLabel">
<option selected>
[[displayText_]]
[[selectedFolder]]
</option>
<option>
[[i18n('selectFolderOption')]]
......
......@@ -28,9 +28,12 @@ Polymer({
properties: {
/**
* The lowest level directory in |selectedFilePath|.
* @private
* @type {string}
*/
displayText_: String,
selectedFolder: {
type: String,
notify: true,
},
/** @type {string} */
selectedFilePath: {
......@@ -42,7 +45,7 @@ Polymer({
/** @override */
created() {
// Default option is 'My files'.
this.displayText_ = this.i18n('myFilesSelectOption');
this.selectedFolder = this.i18n('myFilesSelectOption');
this.browserProxy_ = ScanningBrowserProxyImpl.getInstance();
this.browserProxy_.initialize();
......@@ -56,8 +59,8 @@ Polymer({
onSelectFolder_() {
this.browserProxy_.requestScanToLocation().then(
/* @type {!SelectedPath} */ (selectedPath) => {
// When the select dialog closes, set dropdown back to |displayText_|
// option.
// When the select dialog closes, set dropdown back to
// |selectedFolder| option.
this.$.scanToSelect.selectedIndex = 0;
const baseName = selectedPath.baseName;
......@@ -68,7 +71,7 @@ Polymer({
return;
}
this.displayText_ = baseName;
this.selectedFolder = baseName;
this.selectedFilePath = filePath;
});
},
......
......@@ -60,18 +60,21 @@
width: var(--container-width);
}
.left-panel {
#leftPanel {
margin-inline-end: var(--left-panel-margin-inline-end);
margin-inline-start: var(--left-panel-margin-inline-start);
width: var(--left-panel-width);
}
.panel-container {
#panelContainer {
display: flex;
height: calc(100vh - var(--panel-container-margin-top));
margin-top: var(--panel-container-margin-top);
}
.right-panel {
#rightPanel {
display: flex;
flex-direction: column;
margin-inline-end: var(--right-panel-margin-inline-end);
margin-inline-start: var(--right-panel-margin-inline-start);
padding-inline-end: var(--right-panel-padding-inline-end);
......@@ -90,8 +93,9 @@
#settingsSection {
display: flex;
flex: 1;
flex-direction: column;
height: calc(100vh - var(--panel-container-margin-top));
min-height: 0;
}
#container {
......@@ -165,15 +169,15 @@
}
</style>
<div id="scanningContainer">
<div class="panel-container">
<div class="left-panel">
<div id="panelContainer">
<div id="leftPanel">
<scan-preview id="scanPreview" app-state="[[appState_]]"
object-urls="[[objectUrls_]]" page-number="[[pageNumber_]]"
progress-percent="[[progressPercent_]]"></scan-preview>
</div>
<div class="right-panel">
<div id="rightPanel">
<h1 id="appTitle">[[i18n('appTitle')]]</h1>
<div id="settingsSection" hidden="[[showDoneSection_]]">
<h1 id="appTitle">[[i18n('appTitle')]]</h1>
<div id="container" show-bottom-shadow>
<template is="dom-if" if="[[!showDoneSection_]]">
<scanner-select id="scannerSelect" scanners="[[scanners_]]"
......@@ -185,7 +189,8 @@
selected-source="{{selectedSource}}"></source-select>
<scan-to-select id="scanToSelect"
disabled="[[settingsDisabled_]]"
selected-file-path="{{selectedFilePath}}">
selected-file-path="{{selectedFilePath}}"
selected-folder="{{selectedFolder}}">
</scan-to-select>
<file-type-select id="fileTypeSelect"
disabled="[[settingsDisabled_]]"
......@@ -231,9 +236,10 @@
</div>
</div>
<template is="dom-if" if="[[showDoneSection_]]">
<scan-done-section page-number="[[pageNumber_]]"
<scan-done-section num-files-saved="[[getNumFilesSaved_(pageNumber_)]]"
on-done-click="onDoneClick_" on-file-not-found="onFileNotFound_"
last-scanned-file-path="[[lastScannedFilePath_]]">
last-scanned-file-path="[[lastScannedFilePath_]]"
selected-folder="[[selectedFolder]]">
</scan-done-section>
</template>
</div>
......
......@@ -98,6 +98,12 @@ Polymer({
/** @type {string} */
selectedResolution: String,
/**
* Used to indicate where scanned files are saved when a scan is complete.
* @type {string}
*/
selectedFolder: String,
/**
* Used to determine when certain parts of the app should be shown or hidden
* and enabled or disabled.
......@@ -566,4 +572,15 @@ Polymer({
this.setAppState_(AppState.READY);
window.open('http://support.google.com/chromebook?p=chrome_scanning');
},
/**
* @return {number}
* @private
*/
getNumFilesSaved_() {
return this.selectedFileType ===
chromeos.scanning.mojom.FileType.kPdf.toString() ?
1 :
this.pageNumber_;
}
});
......@@ -39,7 +39,6 @@
<include name="IDR_SCANNING_APP_SCANNING_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_fonts_css.js" use_base_dir="false" type="BINDATA"/>
<include name="IDR_SCANNING_APP_SCAN_PREVIEW_HTML" file="scan_preview.html" type="BINDATA"/>
<include name="IDR_SCANNING_APP_SCAN_PREVIEW_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_preview.js" use_base_dir="false" type="BINDATA"/>
<include name="IDR_SCANNING_APP_ICONS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/icons.js" use_base_dir="false" type="BINDATA"/>
<include name="IDR_SCANNING_APP_ICON_16" file="scanning_app_icon_16.png" type="BINDATA" />
<include name="IDR_SCANNING_APP_ICON_32" file="scanning_app_icon_32.png" type="BINDATA" />
<include name="IDR_SCANNING_APP_ICON_48" file="scanning_app_icon_48.png" type="BINDATA" />
......
<template>
<style>
:host {
--scanning-done-section-title-font-family: "Google Sans", Roboto, sans-serif;
--scanning-done-section-option-font-family: Roboto;
--scanning-helper-text-font-family: Roboto;
--scanning-more-settings-font-family: Roboto;
......@@ -10,7 +9,6 @@
--scanning-section-title-font-family: "Google Sans", Roboto, sans-serif;
--scanning-select-font-family: Roboto;
--scanning-done-section-title-font-size: 22px;
--scanning-done-section-option-font-size: 13px;
--scanning-helper-text-font-size: 13px;
--scanning-more-settings-font-size: 12px;
......@@ -19,7 +17,6 @@
--scanning-section-title-font-size: 22px;
--scanning-select-font-size: 13px;
--scanning-done-section-title-line-height: 28px;
--scanning-done-section-option-line-height: 20px;
--scanning-helper-text-line-height: 20px;
--scanning-more-settings-line-height: 18px;
......@@ -31,7 +28,6 @@
--scanning-regular-font-weight: 400;
--scanning-medium-font-weight: 500;
--scanning-done-section-title-text-color: var(--google-grey-900);
--scanning-done-section-option-text-color: var(--google-grey-900);
--scanning-helper-text-color: var(--google-grey-700);
--scanning-more-settings-text-color: var(--google-grey-700);
......@@ -82,13 +78,6 @@
line-height: var(--scanning-progress-text-line-height);
}
--scanning-done-section-title-font: {
font-family: var(--scanning-done-section-title-font-family);
font-size: var(--scanning-done-section-title-font-size);
font-weight: var(--scanning-medium-font-weight);
line-height: var(--scanning-done-section-title-line-height);
}
--scanning-done-section-option-font: {
font-family: var(--scanning-done-section-option-font-family);
font-size: var(--scanning-done-section-option-font-size);
......
......@@ -96,7 +96,6 @@ void AddScanningAppStrings(content::WebUIDataSource* html_source) {
{"scannerDropdownLabel", IDS_SCANNING_APP_SCANNER_DROPDOWN_LABEL},
{"scanningImagesAriaLabel", IDS_SCANNING_APP_SCANNING_IMAGES_ARIA_LABEL},
{"selectFolderOption", IDS_SCANNING_APP_SELECT_FOLDER_OPTION},
{"showFileLocationLabel", IDS_SCANNING_APP_SHOW_FILE_LOCATION_LABEL},
{"sourceDropdownLabel", IDS_SCANNING_APP_SOURCE_DROPDOWN_LABEL},
{"startScanFailedToast", IDS_SCANNING_APP_START_SCAN_FAILED_TOAST},
{"twoSidedDocFeederOptionText",
......
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