Commit 14ab0224 authored by Wei Lee's avatar Wei Lee Committed by Commit Bot

[CCA] Change the default folder to MyFiles/Camera

Design Doc: go/cros-camera:dd:cca-default-saved-folder

Bug: 1127587
Test: Take a photo and Camera folder is created and the photo is saved
there.

Change-Id: I2701ee5ed82df3df3a0ff2e65ad33f6e6145c3bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2407732
Commit-Queue: Wei Lee <wtlee@chromium.org>
Reviewed-by: default avatarInker Kuo <inker@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809023}
parent 0a0bec54
...@@ -89,11 +89,11 @@ void ChromeCameraAppUIDelegate::SetLaunchDirectory() { ...@@ -89,11 +89,11 @@ void ChromeCameraAppUIDelegate::SetLaunchDirectory() {
// path in it. // path in it.
base::FilePath empty_file_path("/dev/null"); base::FilePath empty_file_path("/dev/null");
auto downloads_folder_path = auto my_files_folder_path =
file_manager::util::GetDownloadsFolderForProfile(profile); file_manager::util::GetMyFilesFolderForProfile(profile);
web_launch::WebLaunchFilesHelper::SetLaunchDirectoryAndLaunchPaths( web_launch::WebLaunchFilesHelper::SetLaunchDirectoryAndLaunchPaths(
web_contents, web_contents->GetURL(), downloads_folder_path, web_contents, web_contents->GetURL(), my_files_folder_path,
std::vector<base::FilePath>{empty_file_path}); std::vector<base::FilePath>{empty_file_path});
web_app::WebAppTabHelper::CreateForWebContents(web_contents); web_app::WebAppTabHelper::CreateForWebContents(web_contents);
} }
...@@ -119,9 +119,9 @@ void ChromeCameraAppUIDelegate::OpenFileInGallery(const std::string& name) { ...@@ -119,9 +119,9 @@ void ChromeCameraAppUIDelegate::OpenFileInGallery(const std::string& name) {
Profile* profile = Profile::FromWebUI(web_ui_); Profile* profile = Profile::FromWebUI(web_ui_);
base::FilePath path = base::FilePath path = file_manager::util::GetMyFilesFolderForProfile(profile)
file_manager::util::GetDownloadsFolderForProfile(profile).Append( .Append("Camera")
name_component); .Append(name_component);
auto&& file_paths = std::vector<base::FilePath>({path}); auto&& file_paths = std::vector<base::FilePath>({path});
apps::mojom::FilePathsPtr launch_files = apps::mojom::FilePathsPtr launch_files =
apps::mojom::FilePaths::New(file_paths); apps::mojom::FilePaths::New(file_paths);
......
...@@ -158,6 +158,7 @@ copy("chrome_camera_app_js_models") { ...@@ -158,6 +158,7 @@ copy("chrome_camera_app_js_models") {
"src/js/models/file_system.js", "src/js/models/file_system.js",
"src/js/models/file_system_entry.js", "src/js/models/file_system_entry.js",
"src/js/models/file_util.js", "src/js/models/file_util.js",
"src/js/models/lazy_directory_entry.js",
"src/js/models/mp4_video_processor.js", "src/js/models/mp4_video_processor.js",
"src/js/models/nop_video_processor.js", "src/js/models/nop_video_processor.js",
"src/js/models/result_saver.js", "src/js/models/result_saver.js",
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
<structure name="IDR_CAMERA_INIT_JS" file="src/js/init.js" type="chrome_html" /> <structure name="IDR_CAMERA_INIT_JS" file="src/js/init.js" type="chrome_html" />
<structure name="IDR_CAMERA_INTENT_JS" file="src/js/intent.js" type="chrome_html" /> <structure name="IDR_CAMERA_INTENT_JS" file="src/js/intent.js" type="chrome_html" />
<structure name="IDR_CAMERA_LAYOUT_JS" file="src/js/views/camera/layout.js" type="chrome_html" /> <structure name="IDR_CAMERA_LAYOUT_JS" file="src/js/views/camera/layout.js" type="chrome_html" />
<structure name="IDR_CAMERA_LAZY_DIRECTORY_ENTRY_JS" file="src/js/models/lazy_directory_entry.js" type="chrome_html" />
<structure name="IDR_CAMERA_MAIN_CSS" file="src/css/main.css" type="chrome_html" /> <structure name="IDR_CAMERA_MAIN_CSS" file="src/css/main.css" type="chrome_html" />
<structure name="IDR_CAMERA_MAIN_HTML" file="src/views/main.html" type="chrome_html" /> <structure name="IDR_CAMERA_MAIN_HTML" file="src/views/main.html" type="chrome_html" />
<structure name="IDR_CAMERA_MAIN_JS" file="src/js/main.js" type="chrome_html" /> <structure name="IDR_CAMERA_MAIN_JS" file="src/js/main.js" type="chrome_html" />
......
...@@ -47,6 +47,7 @@ js_library("compile_resources") { ...@@ -47,6 +47,7 @@ js_library("compile_resources") {
"models/file_system.js", "models/file_system.js",
"models/file_system_entry.js", "models/file_system_entry.js",
"models/file_util.js", "models/file_util.js",
"models/lazy_directory_entry.js",
"models/mp4_video_processor.js", "models/mp4_video_processor.js",
"models/native_file_system_entry.js", "models/native_file_system_entry.js",
"models/nop_video_processor.js", "models/nop_video_processor.js",
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
import {assert, promisify} from '../chrome_util.js'; import {assert, promisify} from '../chrome_util.js';
import {ChromeDirectoryEntry} from '../models/chrome_file_system_entry.js'; import {ChromeDirectoryEntry} from '../models/chrome_file_system_entry.js';
import {getMaybeLazyDirectory} from '../models/lazy_directory_entry.js';
import {Resolution} from '../type.js'; import {Resolution} from '../type.js';
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import {BrowserProxy} from './browser_proxy_interface.js'; import {BrowserProxy} from './browser_proxy_interface.js';
...@@ -39,7 +41,7 @@ class ChromeAppBrowserProxy { ...@@ -39,7 +41,7 @@ class ChromeAppBrowserProxy {
} }
/** @override */ /** @override */
async getExternalDir() { async getCameraDirectory() {
let volumes; let volumes;
try { try {
volumes = await promisify(chrome.fileSystem.getVolumeList)(); volumes = await promisify(chrome.fileSystem.getVolumeList)();
...@@ -67,9 +69,8 @@ class ChromeAppBrowserProxy { ...@@ -67,9 +69,8 @@ class ChromeAppBrowserProxy {
continue; continue;
} }
const rootEntry = new ChromeDirectoryEntry(root); const myFilesDir = new ChromeDirectoryEntry(root);
const entries = await rootEntry.getDirectories(); return getMaybeLazyDirectory(myFilesDir, 'Camera');
return entries.find((entry) => entry.name === 'Downloads') || null;
} }
return null; return null;
} }
......
...@@ -25,7 +25,7 @@ export class BrowserProxy { ...@@ -25,7 +25,7 @@ export class BrowserProxy {
* @return {!Promise<?AbstractDirectoryEntry>} * @return {!Promise<?AbstractDirectoryEntry>}
* @abstract * @abstract
*/ */
async getExternalDir() {} async getCameraDirectory() {}
/** /**
* @param {(string|!Array<string>|!Object)} keys * @param {(string|!Array<string>|!Object)} keys
......
...@@ -16,6 +16,7 @@ import {BackgroundOps} from '../background_ops.js'; ...@@ -16,6 +16,7 @@ import {BackgroundOps} from '../background_ops.js';
import {assert} from '../chrome_util.js'; import {assert} from '../chrome_util.js';
import {NotImplementedError} from '../error.js'; import {NotImplementedError} from '../error.js';
import {Intent} from '../intent.js'; import {Intent} from '../intent.js';
import {getMaybeLazyDirectory} from '../models/lazy_directory_entry.js';
import {NativeDirectoryEntry} from '../models/native_file_system_entry.js'; import {NativeDirectoryEntry} from '../models/native_file_system_entry.js';
import {ChromeHelper} from '../mojo/chrome_helper.js'; import {ChromeHelper} from '../mojo/chrome_helper.js';
import {PerfLogger} from '../perf.js'; import {PerfLogger} from '../perf.js';
...@@ -36,7 +37,7 @@ class WebUIBrowserProxy { ...@@ -36,7 +37,7 @@ class WebUIBrowserProxy {
} }
/** @override */ /** @override */
async getExternalDir() { async getCameraDirectory() {
return new Promise((resolve) => { return new Promise((resolve) => {
const launchQueue = window.launchQueue; const launchQueue = window.launchQueue;
assert(launchQueue !== undefined); assert(launchQueue !== undefined);
...@@ -45,7 +46,9 @@ class WebUIBrowserProxy { ...@@ -45,7 +46,9 @@ class WebUIBrowserProxy {
const dir = const dir =
/** @type {!FileSystemDirectoryHandle} */ (launchParams.files[0]); /** @type {!FileSystemDirectoryHandle} */ (launchParams.files[0]);
assert(dir.kind === 'directory'); assert(dir.kind === 'directory');
resolve(new NativeDirectoryEntry(dir));
const myFilesDir = new NativeDirectoryEntry(dir);
resolve(getMaybeLazyDirectory(myFilesDir, 'Camera'));
}); });
}); });
} }
......
...@@ -209,9 +209,9 @@ export class App { ...@@ -209,9 +209,9 @@ export class App {
metrics.sendLaunchEvent({ackMigrate}); metrics.sendLaunchEvent({ackMigrate});
}); });
const externalDir = filesystem.getExternalDirectory(); const cameraDir = filesystem.getCameraDirectory();
assert(externalDir !== null); assert(cameraDir !== null);
this.galleryButton_.initialize(externalDir); this.galleryButton_.initialize(cameraDir);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
if (error && error.message === 'no-migrate') { if (error && error.message === 'no-migrate') {
......
...@@ -48,17 +48,17 @@ let internalDir = null; ...@@ -48,17 +48,17 @@ let internalDir = null;
let internalTempDir = null; let internalTempDir = null;
/** /**
* Directory in the external file system. * Camera directory in the external file system.
* @type {?AbstractDirectoryEntry} * @type {?AbstractDirectoryEntry}
*/ */
let externalDir = null; let cameraDir = null;
/** /**
* Gets global external directory used by CCA. * Gets camera directory used by CCA.
* @return {?AbstractDirectoryEntry} * @return {?AbstractDirectoryEntry}
*/ */
export function getExternalDirectory() { export function getCameraDirectory() {
return externalDir; return cameraDir;
} }
/** /**
...@@ -86,11 +86,11 @@ function initInternalTempDir() { ...@@ -86,11 +86,11 @@ function initInternalTempDir() {
} }
/** /**
* Initializes the directory in the external file system. * Initializes the camera directory in the external file system.
* @return {!Promise<?AbstractDirectoryEntry>} Promise for the directory result. * @return {!Promise<?AbstractDirectoryEntry>} Promise for the directory result.
*/ */
async function initExternalDir() { async function initCameraDirectory() {
return browserProxy.getExternalDir(); return browserProxy.getCameraDirectory();
} }
/** /**
...@@ -122,6 +122,7 @@ function regulatePictureName(entry) { ...@@ -122,6 +122,7 @@ function regulatePictureName(entry) {
* @return {!Promise} Promise for the operation. * @return {!Promise} Promise for the operation.
*/ */
async function migratePictures() { async function migratePictures() {
assert(cameraDir !== null);
const internalEntries = await internalDir.getFiles(); const internalEntries = await internalDir.getFiles();
for (const entry of internalEntries) { for (const entry of internalEntries) {
if (entry.name.startsWith(THUMBNAIL_PREFIX)) { if (entry.name.startsWith(THUMBNAIL_PREFIX)) {
...@@ -129,8 +130,7 @@ async function migratePictures() { ...@@ -129,8 +130,7 @@ async function migratePictures() {
continue; continue;
} }
const name = regulatePictureName(entry); const name = regulatePictureName(entry);
assert(externalDir !== null); await entry.moveTo(cameraDir, name);
await entry.moveTo(externalDir, name);
} }
} }
...@@ -146,8 +146,8 @@ export async function initialize() { ...@@ -146,8 +146,8 @@ export async function initialize() {
internalTempDir = await initInternalTempDir(); internalTempDir = await initInternalTempDir();
assert(internalTempDir !== null); assert(internalTempDir !== null);
externalDir = await initExternalDir(); cameraDir = await initCameraDirectory();
assert(externalDir !== null); assert(cameraDir !== null);
} }
/** /**
...@@ -198,9 +198,7 @@ export async function checkMigration(promptMigrate) { ...@@ -198,9 +198,7 @@ export async function checkMigration(promptMigrate) {
* @return {!Promise<?AbstractFileEntry>} Promise for the result. * @return {!Promise<?AbstractFileEntry>} Promise for the result.
*/ */
export async function saveBlob(blob, name) { export async function saveBlob(blob, name) {
assert(externalDir !== null); const file = await cameraDir.createFile(name);
const file = await externalDir.createFile(name);
assert(file !== null); assert(file !== null);
await file.write(blob); await file.write(blob);
...@@ -213,9 +211,8 @@ export async function saveBlob(blob, name) { ...@@ -213,9 +211,8 @@ export async function saveBlob(blob, name) {
* @throws {!Error} If failed to create video file. * @throws {!Error} If failed to create video file.
*/ */
export async function createVideoFile() { export async function createVideoFile() {
assert(externalDir !== null);
const name = new Filenamer().newVideoName(); const name = new Filenamer().newVideoName();
const file = await externalDir.createFile(name); const file = await cameraDir.createFile(name);
if (file === null) { if (file === null) {
throw new Error('Failed to create video temp file.'); throw new Error('Failed to create video temp file.');
} }
...@@ -248,7 +245,7 @@ export async function createPrivateTempVideoFile() { ...@@ -248,7 +245,7 @@ export async function createPrivateTempVideoFile() {
* entries. * entries.
*/ */
export async function getEntries() { export async function getEntries() {
const entries = await externalDir.getFiles(); const entries = await cameraDir.getFiles();
return entries.filter((entry) => { return entries.filter((entry) => {
if (!hasVideoPrefix(entry) && !hasImagePrefix(entry)) { if (!hasVideoPrefix(entry) && !hasImagePrefix(entry)) {
return false; return false;
......
...@@ -125,6 +125,8 @@ export class AbstractDirectoryEntry extends AbstractFileSystemEntry { ...@@ -125,6 +125,8 @@ export class AbstractDirectoryEntry extends AbstractFileSystemEntry {
/** /**
* Gets the directory given by its |name|. If the directory is not found, * Gets the directory given by its |name|. If the directory is not found,
* create one if |createIfNotExist| is true. * create one if |createIfNotExist| is true.
* TODO(crbug.com/1127587): Split this method to getDirectory() and
* createDirectory().
* @param {{name: string, createIfNotExist: boolean}} params * @param {{name: string, createIfNotExist: boolean}} params
* @return {!Promise<?AbstractDirectoryEntry>} The entry of the found/created * @return {!Promise<?AbstractDirectoryEntry>} The entry of the found/created
* directory. * directory.
......
// 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 {
AbstractDirectoryEntry, // eslint-disable-line no-unused-vars
} from './file_system_entry.js';
/**
* Gets directory entry by given |name| under |parentDir| directory. If the
* directory does not exist, returns a lazy directory which will only be created
* once there is any file written in it.
* @param {!AbstractDirectoryEntry} parentDir Parent directory.
* @param {string} name Name of the target directory.
* @return {!Promise<!AbstractDirectoryEntry>}
*/
export async function getMaybeLazyDirectory(parentDir, name) {
const targetDir =
await parentDir.getDirectory({name, createIfNotExist: false});
return targetDir !== null ? targetDir :
new LazyDirectoryEntry(parentDir, name);
}
/**
* A directory entry which will only create itself if there is any
* file/directory created under it.
* @implements {AbstractDirectoryEntry}
*/
class LazyDirectoryEntry {
/**
* @param {!AbstractDirectoryEntry} parentDirectory
* @param {string} name
*/
constructor(parentDirectory, name) {
/**
* @type {!AbstractDirectoryEntry}
* @private
*/
this.parent_ = parentDirectory;
/**
* @type {string}
* @private
*/
this.name_ = name;
/**
* @type {?AbstractDirectoryEntry}
* @private
*/
this.directory_ = null;
/**
* @type {?Promise<!AbstractDirectoryEntry>}
* @private
*/
this.creatingDirectory_ = null;
}
/**
* The name of the directory that will lazily created.
* @return {string}
*/
get name() {
return this.name_;
}
/**
* @override
*/
async getFiles() {
if (this.directory_ === null) {
return [];
}
return this.directory_.getFiles();
}
/**
* @override
*/
async getDirectories() {
if (this.directory_ === null) {
return [];
}
return this.directory_.getDirectories();
}
/**
* @override
*/
async getFile(name) {
if (this.directory_ === null) {
return null;
}
return this.directory_.getFile(name);
}
/**
* @override
*/
async createFile(name) {
const dir = await this.getRealDirectory_();
return dir.createFile(name);
}
/**
* @override
*/
async getDirectory({name, createIfNotExist}) {
if (this.directory_ === null && !createIfNotExist) {
return null;
}
const dir = await this.getRealDirectory_();
return dir.getDirectory({name, createIfNotExist});
}
/**
* Gets the directory which this entry points to. Create it if it does not
* exist.
* @return {!Promise<!AbstractDirectoryEntry>}
* @private
*/
async getRealDirectory_() {
if (this.creatingDirectory_ === null) {
this.creatingDirectory_ =
(async () => /** @type {!AbstractDirectoryEntry} */ (
await this.parent_.getDirectory(
{name: this.name_, createIfNotExist: true})))();
}
this.directory_ =
/** @type {!AbstractDirectoryEntry} */ (await this.creatingDirectory_);
return this.directory_;
}
}
...@@ -166,11 +166,18 @@ export class NativeDirectoryEntry extends NativeFileSystemEntry { ...@@ -166,11 +166,18 @@ export class NativeDirectoryEntry extends NativeFileSystemEntry {
* @override * @override
*/ */
async getDirectory({name, createIfNotExist}) { async getDirectory({name, createIfNotExist}) {
const handle = try {
await this.handle_.getDirectoryHandle(name, {create: createIfNotExist}); const handle = await this.handle_.getDirectoryHandle(
assert(handle !== null); name, {create: createIfNotExist});
return new NativeDirectoryEntry( assert(handle !== null);
/** @type {!FileSystemDirectoryHandle} */ (handle)); return new NativeDirectoryEntry(
/** @type {!FileSystemDirectoryHandle} */ (handle));
} catch (error) {
if (!createIfNotExist && error.name === 'NotFoundError') {
return null;
}
throw error;
}
} }
/** /**
......
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