Commit 9f7a4c7e authored by Gavin Williams's avatar Gavin Williams Committed by Commit Bot

scanning: Open select dialog to choose save directory

This change adds a option to the Scan To dropdown that opens the select
dialog and lets the user choose the directory to save completed scans.

Screenshot: http://screen/dnTcAPoV48BZmDJ

Bug: 1059779
Change-Id: Id35ed06fff8fe1f4ca4c3e0c8c6d5ace852ea750
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2508151
Commit-Queue: Gavin Williams <gavinwill@chromium.org>
Reviewed-by: default avatarZentaro Kavanagh <zentaro@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarJimmy Gong <jimmyxgong@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822926}
parent 7cc200c8
......@@ -26,6 +26,7 @@ ScanningAppBrowserTest.prototype = {
extraLibraries: [
'//third_party/mocha/mocha.js',
'//chrome/test/data/webui/mocha_adapter.js',
'//chrome/test/data/webui/test_browser_proxy.js',
'//ui/webui/resources/js/assert.js',
'//ui/webui/resources/js/promise_resolver.js',
],
......
......@@ -11,7 +11,12 @@ import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min
import {setScanServiceForTesting} from 'chrome://scanning/mojo_interface_provider.js';
import {ScannerArr} from 'chrome://scanning/scanning_app_types.js';
import {getColorModeString, getPageSizeString, getSourceTypeString, tokenToString} from 'chrome://scanning/scanning_app_util.js';
import {ScanningBrowserProxyImpl} from 'chrome://scanning/scanning_browser_proxy.js';
import {flushTasks} from '../../test_util.m.js';
import * as utils from './scanning_app_test_utils.js';
import {TestScanningBrowserProxy} from './test_scanning_browser_proxy.js';
const ColorMode = {
BLACK_AND_WHITE: chromeos.scanning.mojom.ColorMode.kBlackAndWhite,
......@@ -287,7 +292,7 @@ suite('ScanningAppTest', () => {
scanningApp.$$('#resolutionSelect').$$('select').disabled);
assertFalse(scanningApp.$$('#scanButton').disabled);
assertEquals(
'Scan complete! File(s) saved to My files.',
'Scan complete! File(s) saved to /home/chronos/user/MyFiles.',
scanningApp.$$('#statusText').textContent.trim());
});
});
......@@ -724,10 +729,19 @@ suite('ScanToSelectTest', () => {
/** @type {?ScanToSelectElement} */
let scanToSelect = null;
/** {string} */
/** @type {?TestScanningBrowserProxy} */
let scanningBrowserProxy = null;
/** @const {string} */
const myFiles = 'My files';
/** @const {string} */
const selectFolderText = 'Select folder in Files app…';
setup(() => {
scanningBrowserProxy = new TestScanningBrowserProxy();
ScanningBrowserProxyImpl.instance_ = scanningBrowserProxy;
scanToSelect = document.createElement('scan-to-select');
assertTrue(!!scanToSelect);
document.body.appendChild(scanToSelect);
......@@ -741,11 +755,83 @@ suite('ScanToSelectTest', () => {
});
test('initializeScanToSelect', () => {
// The dropdown should be disabled and only have one entry for 'My files'.
const select = scanToSelect.$$('select');
assertTrue(!!select);
assertTrue(select.disabled);
assertEquals(1, select.length);
assertFalse(select.disabled);
assertEquals(2, select.length);
assertEquals(myFiles, select.options[0].textContent.trim());
assertEquals(selectFolderText, select.options[1].textContent.trim());
});
// Verifies the 'Scan To' dropdown updates when the user chooses a folder in
// the select dialog.
test('selectFolderDialog', () => {
const googleDrivePath = '/this/is/a/Google/Drive';
const googleDrive = 'Drive';
const myDownloadsPath = '/this/is/a/test/directory/My Downloads';
const myDownloads = 'My Downloads';
// Simulate clicking the 'Select folder' option.
scanningBrowserProxy.setSelectedPath(
{baseName: myDownloads, filePath: myDownloadsPath});
const select = scanToSelect.$$('select');
select.selectedIndex = 1;
select.dispatchEvent(new CustomEvent('change'));
return flushTasks()
.then(() => {
assertEquals(myDownloadsPath, scanToSelect.selectedFilePath);
assertEquals(
myDownloads,
select.options[select.selectedIndex].textContent.trim());
assertEquals(0, select.selectedIndex);
scanningBrowserProxy.setSelectedPath(
{baseName: googleDrive, filePath: googleDrivePath});
select.selectedIndex = 1;
select.dispatchEvent(new CustomEvent('change'));
return flushTasks();
})
.then(() => {
assertEquals(googleDrivePath, scanToSelect.selectedFilePath);
assertEquals(
googleDrive,
select.options[select.selectedIndex].textContent.trim());
assertEquals(0, select.selectedIndex);
});
});
// Verifys the 'Scan To' dropdown retains the previous selection when the user
// cancels the select dialog.
test('cancelSelectDialog', () => {
const myDownloadsPath = '/this/is/a/test/directory/My Downloads';
const myDownloads = 'My Downloads';
// Simulate clicking the 'Select folder' option.
scanningBrowserProxy.setSelectedPath(
{baseName: myDownloads, filePath: myDownloadsPath});
const select = scanToSelect.$$('select');
select.selectedIndex = 1;
select.dispatchEvent(new CustomEvent('change'));
return flushTasks()
.then(() => {
assertEquals(myDownloadsPath, scanToSelect.selectedFilePath);
assertEquals(
myDownloads,
select.options[select.selectedIndex].textContent.trim());
assertEquals(0, select.selectedIndex);
// Simulate canceling the select dialog
scanningBrowserProxy.setSelectedPath({baseName: '', filePath: ''});
select.selectedIndex = 1;
select.dispatchEvent(new CustomEvent('change'));
return flushTasks();
})
.then(() => {
assertEquals(myDownloadsPath, scanToSelect.selectedFilePath);
assertEquals(
myDownloads,
select.options[select.selectedIndex].textContent.trim());
assertEquals(0, select.selectedIndex);
});
});
});
// 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 {ScanningBrowserProxy, SelectedPath} from 'chrome://scanning/scanning_browser_proxy.js';
import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
/**
* Test version of ScanningBrowserProxy.
* @implements {ScanningBrowserProxy}
*/
export class TestScanningBrowserProxy extends TestBrowserProxy {
constructor() {
super([
'initialize',
'requestScanToLocation',
]);
/** @private {?SelectedPath} */
this.selectedPath_ = null;
}
/** @override */
initialize() {
this.methodCalled('initialize');
}
/**
* @return {!Promise}
* @override
*/
requestScanToLocation() {
this.methodCalled('requestScanToLocation');
return Promise.resolve(this.selectedPath_);
}
/** @param {!SelectedPath} selectedPath */
setSelectedPath(selectedPath) {
this.selectedPath_ = selectedPath;
}
}
......@@ -504,6 +504,9 @@ Try tapping the mic to ask me anything.
<message name="IDS_SCANNING_APP_MY_FILES_SELECT_OPTION" desc="The text displayed in the Scan To dropdown when the user chooses the local 'My files' folder from the select dialog.">
My files
</message>
<message name="IDS_SCANNING_APP_SELECT_FOLDER_OPTION" desc="The text displayed in the Scan To dropdown option which the user can click to open the select dialog for choosing the folder to save completed scans.">
Select folder in Files app...
</message>
<!-- Diagnostics App -->
<!-- TODO(michaelcheco): Update with finalized copies of the strings -->
......
b4297e59d6ccdb7a8a4df8572c1ec6bccf64ad57
\ No newline at end of file
......@@ -3,9 +3,12 @@
<div slot="settings">
<!-- TODO(jschettler): Verify this meets a11y expecations (e.g. ChromeVox
should announce when a new option is focused). -->
<select class="md-select" disabled="[[disabled]]">
<select id="scanToSelect" class="md-select" disabled="[[disabled]]" on-change="onSelectFolder_">
<option selected>
[[displayText_]]
</option>
<option>
[[i18n('myFilesSelectOption')]]
[[i18n('selectFolderOption')]]
</option>
</select>
</div>
......
......@@ -3,10 +3,12 @@
// found in the LICENSE file.
import './scan_settings_section.js';
import './strings.m.js';
import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ScanningBrowserProxy, ScanningBrowserProxyImpl, SelectedPath} from './scanning_browser_proxy.js';
import {SelectBehavior} from './select_behavior.js';
/**
......@@ -19,4 +21,58 @@ Polymer({
_template: html`{__html_template__}`,
behaviors: [I18nBehavior, SelectBehavior],
/** @private {?ScanningBrowserProxy}*/
browserProxy_: null,
properties: {
/**
* The lowest level directory in |selectedFilePath|.
* @private
*/
displayText_: String,
/** @type {string} */
selectedFilePath: {
type: String,
notify: true,
},
},
/** @override */
created() {
// Default option is 'My files'.
this.displayText_ = this.i18n('myFilesSelectOption');
// The dropdown always has 2 options.
this.onNumOptionsChange(2);
this.browserProxy_ = ScanningBrowserProxyImpl.getInstance();
this.browserProxy_.initialize();
},
/**
* Opens the select dialog and updates the dropdown to the user's selected
* directory.
* @private
*/
onSelectFolder_() {
this.browserProxy_.requestScanToLocation().then(
/* @type {!SelectedPath} */ (selectedPath) => {
// When the select dialog closes, set dropdown back to |displayText_|
// option.
this.$.scanToSelect.selectedIndex = 0;
const baseName = selectedPath.baseName;
const filePath = selectedPath.filePath;
// When the select dialog is canceled, |baseName| and |filePath| will
// be empty.
if (!baseName || !filePath) {
return;
}
this.displayText_ = baseName;
this.selectedFilePath = filePath;
});
},
});
......@@ -33,8 +33,9 @@
<source-select id="sourceSelect" sources="[[capabilities_.sources]]"
settings-disabled="[[settingsDisabled_]]"
selected-source="{{selectedSource}}"></source-select>
<scan-to-select id="scanToSelect"
settings-disabled="[[settingsDisabled_]]"></scan-to-select>
<scan-to-select id="scanToSelect" settings-disabled="[[settingsDisabled_]]"
selected-file-path="{{selectedFilePath}}">
</scan-to-select>
<file-type-select id="fileTypeSelect" settings-disabled="[[settingsDisabled_]]"
selected-file-type="{{selectedFileType}}"></file-type-select>
<color-mode-select id="colorModeSelect"
......
......@@ -24,6 +24,12 @@ import {getScanService} from './mojo_interface_provider.js';
import {ScannerArr} from './scanning_app_types.js';
import {colorModeFromString, pageSizeFromString, tokenToString} from './scanning_app_util.js';
/**
* The default save directory for completed scans.
* @const {string}
*/
const DEFAULT_SAVE_DIRECTORY = '/home/chronos/user/MyFiles';
/**
* @fileoverview
* 'scanning-app' is used to interact with connected scanners.
......@@ -54,7 +60,7 @@ Polymer({
/** @type {string} */
selectedScannerId: {
type: String,
observer: 'onSelectedScannerIdChange_'
observer: 'onSelectedScannerIdChange_',
},
/**
......@@ -69,6 +75,9 @@ Polymer({
/** @type {?string} */
selectedFileType: String,
/** @type {string} */
selectedFilePath: String,
/** @type {?string} */
selectedColorMode: String,
......@@ -116,6 +125,7 @@ Polymer({
/** @override */
created() {
this.scanService_ = getScanService();
this.selectedFilePath = DEFAULT_SAVE_DIRECTORY;
},
/** @override */
......@@ -225,11 +235,10 @@ Polymer({
this.scanButtonDisabled_ = true;
// TODO(jschettler): Use the selected file type when ScanService supports
// it. Use the selected scan-to path when the corresponding dropdown is
// added.
// it.
const settings = {
'sourceName': this.selectedSource,
'scanToPath': {'path': '/home/chronos/user/MyFiles'},
'scanToPath': {'path': this.selectedFilePath},
'fileType': chromeos.scanning.mojom.FileType.kPng,
'colorMode': colorModeFromString(this.selectedColorMode),
'pageSize': pageSizeFromString(this.selectedPageSize),
......@@ -249,7 +258,8 @@ Polymer({
*/
onScanCompleted_(response) {
if (response.success) {
this.statusText_ = 'Scan complete! File(s) saved to My files.';
this.statusText_ =
'Scan complete! File(s) saved to ' + this.selectedFilePath + '.';
} else {
this.statusText_ = 'Scan failed.';
}
......
......@@ -10,14 +10,22 @@
import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
/**
* @typedef {{
* baseName: string,
* filePath: string,
* }}
*/
export let SelectedPath;
/** @interface */
class ScanningBrowserProxy {
export class ScanningBrowserProxy {
/** Initialize ScanningHandler. */
initialize() {}
/**
* Requests the user to choose the directory to save scans.
* @return {!Promise<string>}
* @return {!Promise<!SelectedPath>}
*/
requestScanToLocation() {}
}
......
......@@ -62,6 +62,7 @@ void AddScanningAppStrings(content::WebUIDataSource* html_source) {
{"resolutionOptionText", IDS_SCANNING_APP_RESOLUTION_OPTION_TEXT},
{"scanToDropdownLabel", IDS_SCANNING_APP_SCAN_TO_DROPDOWN_LABEL},
{"scannerDropdownLabel", IDS_SCANNING_APP_SCANNER_DROPDOWN_LABEL},
{"selectFolderOption", IDS_SCANNING_APP_SELECT_FOLDER_OPTION},
{"sourceDropdownLabel", IDS_SCANNING_APP_SOURCE_DROPDOWN_LABEL}};
for (const auto& str : kLocalizedStrings)
......
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