Commit a3aeee0c authored by Wenzhao Zang's avatar Wenzhao Zang Committed by Commit Bot

cros: Implement 'My Photos' section in new wallpaper picker (Part II)

The old wallpaper picker must generate and save a thumbnail for each
custom wallpaper, so that they can persist even after the original
image file is deleted. This is not needed for the new picker:
the thumbnails under 'My Photos' section are real-time representation
of the files under Downloads. We do not want to cache or save them
since the files may change all the time.

Therefore, to simplify the logic, we only need to read the image data,
and then use CSS to resize the image to display as the thumbnail
(the same with backdrop wallpapers for consistency).

The new wallpaper picker doesn't display the thumbnail of the history
custom wallpaper like the old picker. So basically the 'generate and
save thumbnail' code path can be deprecated in the future.

Bug: 809793
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Ibf00bec636a52081c77cd7cf1b94e934fd97b11c
Reviewed-on: https://chromium-review.googlesource.com/912282Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarToni Barzic <tbarzic@chromium.org>
Commit-Queue: Wenzhao (Colin) Zang <wzang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536950}
parent b53ef78a
...@@ -69,6 +69,7 @@ namespace record_wallpaper_uma = wallpaper_private::RecordWallpaperUMA; ...@@ -69,6 +69,7 @@ namespace record_wallpaper_uma = wallpaper_private::RecordWallpaperUMA;
namespace get_collections_info = wallpaper_private::GetCollectionsInfo; namespace get_collections_info = wallpaper_private::GetCollectionsInfo;
namespace get_images_info = wallpaper_private::GetImagesInfo; namespace get_images_info = wallpaper_private::GetImagesInfo;
namespace get_local_image_paths = wallpaper_private::GetLocalImagePaths; namespace get_local_image_paths = wallpaper_private::GetLocalImagePaths;
namespace get_local_image_data = wallpaper_private::GetLocalImageData;
namespace { namespace {
...@@ -928,3 +929,41 @@ void WallpaperPrivateGetLocalImagePathsFunction::OnGetImagePathsComplete( ...@@ -928,3 +929,41 @@ void WallpaperPrivateGetLocalImagePathsFunction::OnGetImagePathsComplete(
const std::vector<std::string>& image_paths) { const std::vector<std::string>& image_paths) {
Respond(ArgumentList(get_local_image_paths::Results::Create(image_paths))); Respond(ArgumentList(get_local_image_paths::Results::Create(image_paths)));
} }
WallpaperPrivateGetLocalImageDataFunction::
WallpaperPrivateGetLocalImageDataFunction() = default;
WallpaperPrivateGetLocalImageDataFunction::
~WallpaperPrivateGetLocalImageDataFunction() = default;
ExtensionFunction::ResponseAction
WallpaperPrivateGetLocalImageDataFunction::Run() {
std::unique_ptr<get_local_image_data::Params> params(
get_local_image_data::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
// TODO(crbug.com/811564): Create file backed blob instead.
auto image_data = std::make_unique<std::string>();
std::string* image_data_ptr = image_data.get();
base::PostTaskAndReplyWithResult(
WallpaperFunctionBase::GetNonBlockingTaskRunner(), FROM_HERE,
base::BindOnce(&base::ReadFileToString,
base::FilePath(params->image_path), image_data_ptr),
base::BindOnce(
&WallpaperPrivateGetLocalImageDataFunction::OnReadImageDataComplete,
this, std::move(image_data)));
return RespondLater();
}
void WallpaperPrivateGetLocalImageDataFunction::OnReadImageDataComplete(
std::unique_ptr<std::string> image_data,
bool success) {
if (!success) {
Respond(Error("Reading image data failed."));
return;
}
Respond(ArgumentList(get_local_image_data::Results::Create(
std::vector<char>(image_data->begin(), image_data->end()))));
}
...@@ -317,7 +317,7 @@ class WallpaperPrivateGetCollectionsInfoFunction ...@@ -317,7 +317,7 @@ class WallpaperPrivateGetCollectionsInfoFunction
protected: protected:
~WallpaperPrivateGetCollectionsInfoFunction() override; ~WallpaperPrivateGetCollectionsInfoFunction() override;
// ExtensionFunction: // UIThreadExtensionFunction:
ResponseAction Run() override; ResponseAction Run() override;
private: private:
...@@ -343,7 +343,7 @@ class WallpaperPrivateGetImagesInfoFunction : public UIThreadExtensionFunction { ...@@ -343,7 +343,7 @@ class WallpaperPrivateGetImagesInfoFunction : public UIThreadExtensionFunction {
protected: protected:
~WallpaperPrivateGetImagesInfoFunction() override; ~WallpaperPrivateGetImagesInfoFunction() override;
// ExtensionFunction: // UIThreadExtensionFunction:
ResponseAction Run() override; ResponseAction Run() override;
private: private:
...@@ -371,7 +371,7 @@ class WallpaperPrivateGetLocalImagePathsFunction ...@@ -371,7 +371,7 @@ class WallpaperPrivateGetLocalImagePathsFunction
protected: protected:
~WallpaperPrivateGetLocalImagePathsFunction() override; ~WallpaperPrivateGetLocalImagePathsFunction() override;
// ExtensionFunction: // UIThreadExtensionFunction:
ResponseAction Run() override; ResponseAction Run() override;
private: private:
...@@ -381,4 +381,25 @@ class WallpaperPrivateGetLocalImagePathsFunction ...@@ -381,4 +381,25 @@ class WallpaperPrivateGetLocalImagePathsFunction
DISALLOW_COPY_AND_ASSIGN(WallpaperPrivateGetLocalImagePathsFunction); DISALLOW_COPY_AND_ASSIGN(WallpaperPrivateGetLocalImagePathsFunction);
}; };
class WallpaperPrivateGetLocalImageDataFunction
: public UIThreadExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("wallpaperPrivate.getLocalImageData",
WALLPAPERPRIVATE_GETLOCALIMAGEDATA)
WallpaperPrivateGetLocalImageDataFunction();
protected:
~WallpaperPrivateGetLocalImageDataFunction() override;
// UIThreadExtensionFunction:
ResponseAction Run() override;
private:
// Responds with the image data or an error message.
void OnReadImageDataComplete(std::unique_ptr<std::string> image_data,
bool success);
DISALLOW_COPY_AND_ASSIGN(WallpaperPrivateGetLocalImageDataFunction);
};
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_WALLPAPER_PRIVATE_API_H_ #endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_WALLPAPER_PRIVATE_API_H_
...@@ -411,6 +411,32 @@ WallpaperUtil.getOnlineWallpaperHighResolutionSuffix = function() { ...@@ -411,6 +411,32 @@ WallpaperUtil.getOnlineWallpaperHighResolutionSuffix = function() {
Constants.HighResolutionSuffix; Constants.HighResolutionSuffix;
}; };
/**
* Creates a blob of type 'image/png'.
* @param {string} data The image data.
*/
WallpaperUtil.createPngBlob = function(data) {
return new Blob([new Int8Array(data)], {'type': 'image/png'});
};
/**
* Displays the image by creating an image blob.
* @param {Object} imageElement The image element.
* @param {string} data The image data.
* @param {function} opt_callback An optional callback, called after the image
* finishes loading.
*/
WallpaperUtil.displayImage = function(imageElement, data, opt_callback) {
imageElement.src =
window.URL.createObjectURL(WallpaperUtil.createPngBlob(data));
imageElement.addEventListener('load', function(e) {
if (opt_callback)
opt_callback();
// Revoke the url since it won't be used anymore after the image is loaded.
window.URL.revokeObjectURL(imageElement.src);
});
};
/** /**
* Runs chrome.test.sendMessage in test environment. Does nothing if running * Runs chrome.test.sendMessage in test environment. Does nothing if running
* in production environment. * in production environment.
......
...@@ -94,53 +94,143 @@ cr.define('wallpapers', function() { ...@@ -94,53 +94,143 @@ cr.define('wallpapers', function() {
window.setTimeout(this.callback_.bind(this, this.dataModelId_), 0); window.setTimeout(this.callback_.bind(this, this.dataModelId_), 0);
break; break;
case Constants.WallpaperSourceEnum.Custom: case Constants.WallpaperSourceEnum.Custom:
var errorHandler = function(e) { if (loadTimeData.getBoolean('useNewWallpaperPicker')) {
self.callback_(self.dataModelId_); this.decorateCustomWallpaper_(
console.error('Can not access file system.'); imageEl, this.dataItem,
}; this.callback_.bind(this, this.dataModelId_));
var wallpaperDirectories = WallpaperDirectories.getInstance(); } else {
var getThumbnail = function(fileName) { this.decorateCustomWallpaperForOldPicker_(
var setURL = function(fileEntry) { imageEl, this.dataItem,
imageEl.src = fileEntry.toURL(); this.callback_.bind(this, this.dataModelId_));
self.callback_( }
self.dataModelId_, self.dataItem.wallpaperId, imageEl);
};
var fallback = function() {
wallpaperDirectories.getDirectory(
Constants.WallpaperDirNameEnum.ORIGINAL, function(dirEntry) {
dirEntry.getFile(
fileName, {create: false}, setURL, errorHandler);
}, errorHandler);
};
var success = function(dirEntry) {
dirEntry.getFile(fileName, {create: false}, setURL, fallback);
};
wallpaperDirectories.getDirectory(
Constants.WallpaperDirNameEnum.THUMBNAIL, success,
errorHandler);
};
getThumbnail(self.dataItem.baseURL);
break; break;
case Constants.WallpaperSourceEnum.OEM: case Constants.WallpaperSourceEnum.OEM:
case Constants.WallpaperSourceEnum.Online: case Constants.WallpaperSourceEnum.Online:
chrome.wallpaperPrivate.getThumbnail( this.decorateOnlineOrOEMWallpaper_(
this.dataItem.baseURL, this.dataItem.source, function(data) { imageEl, this.dataItem,
this.callback_.bind(this, this.dataModelId_));
break;
case Constants.WallpaperSourceEnum.Daily:
case Constants.WallpaperSourceEnum.ThirdParty:
default:
// It's impossible to manually select a DAILY or THIRDPARTY type
// wallpaper.
console.error('Unsupported wallpaper source.');
// Delay dispatching the completion callback until all items have
// begun loading and are tracked.
window.setTimeout(this.callback_.bind(this, this.dataModelId_), 0);
}
},
/**
* Initializes the grid item for custom wallpapers. Used by the new
* wallpaper picker.
* @param {Object} imageElement The image element.
* @param {{filePath: string, baseURL: string, layout: string,
* source: string, availableOffline: boolean}
* dataItem The info related to the wallpaper image.
* @param {function} callback The callback function.
* @private
*/
decorateCustomWallpaper_(imageElement, dataItem, callback) {
if (dataItem.source != Constants.WallpaperSourceEnum.Custom) {
console.error(
'|decorateCustomWallpaper_| is called but the wallpaper source ' +
'is not custom.');
return;
}
// Read the image data from |filePath|.
chrome.wallpaperPrivate.getLocalImageData(
dataItem.filePath, imageData => {
if (chrome.runtime.lastError || !imageData) {
// TODO(crbug.com/810892): Decide the UI: either hide the grid or
// show an error icon.
console.error(
'Initialization of custom wallpaper grid failed for path ' +
dataItem.filePath);
callback(null /*opt_wallpaperId=*/, imageElement);
return;
}
// |opt_wallpaperId| is used as the key to cache the image data, but
// we do not want to cache local image data since it may change
// frequently.
WallpaperUtil.displayImage(
imageElement, imageData,
callback.bind(null /*opt_wallpaperId=*/, imageElement));
});
},
/**
* Initializes the grid item for custom wallpapers. Used by the old
* wallpaper picker (to be deprecated).
* @param {Object} imageElement The image element.
* @param {{filePath: string, baseURL: string, layout: string,
* source: string, availableOffline: boolean}
* dataItem The info related to the wallpaper image.
* @param {function} callback The callback function.
* @private
*/
decorateCustomWallpaperForOldPicker_(imageElement, dataItem, callback) {
if (dataItem.source != Constants.WallpaperSourceEnum.Custom) {
console.error(
'|decorateCustomWallpaperForOldPicker_| is called but the ' +
'wallpaper source is not custom.');
return;
}
var errorHandler = function(e) {
console.error('Can not access file system.');
callback();
};
var setURL = function(fileEntry) {
imageElement.src = fileEntry.toURL();
callback(dataItem.wallpaperId, imageElement);
};
var wallpaperDirectories = WallpaperDirectories.getInstance();
var fallback = function() {
wallpaperDirectories.getDirectory(
Constants.WallpaperDirNameEnum.ORIGINAL, function(dirEntry) {
dirEntry.getFile(
dataItem.baseURL, {create: false}, setURL, errorHandler);
}, errorHandler);
};
var success = function(dirEntry) {
dirEntry.getFile(dataItem.baseURL, {create: false}, setURL, fallback);
};
wallpaperDirectories.getDirectory(
Constants.WallpaperDirNameEnum.THUMBNAIL, success, errorHandler);
},
/**
* Initializes the grid item for online or OEM wallpapers.
* @param {Object} imageElement The image element.
* @param {{filePath: string, baseURL: string, layout: string,
* source: string, availableOffline: boolean}
* dataItem The info related to the wallpaper image.
* @param {function} callback The callback function.
* @private
*/
decorateOnlineOrOEMWallpaper_(imageElement, dataItem, callback) {
if (dataItem.source != Constants.WallpaperSourceEnum.Online &&
dataItem.source != Constants.WallpaperSourceEnum.OEM) {
console.error(
'|decorateOnlineOrOEMWallpaper_| is called but the wallpaper ' +
'source is not online or OEM.');
return;
}
chrome.wallpaperPrivate
.getThumbnail(
dataItem.baseURL, dataItem.source, data => {
if (data) { if (data) {
var blob = WallpaperUtil.displayImage(
new Blob([new Int8Array(data)], {'type': 'image\/png'}); imageElement, data,
imageEl.src = window.URL.createObjectURL(blob); callback.bind(dataItem.wallpaperId, imageElement));
imageEl.addEventListener('load', function(e) {
self.callback_(
self.dataModelId_, self.dataItem.wallpaperId, imageEl);
window.URL.revokeObjectURL(this.src);
});
} else if ( } else if (
self.dataItem.source == dataItem.source == Constants.WallpaperSourceEnum.Online) {
Constants.WallpaperSourceEnum.Online) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open( xhr.open(
'GET', 'GET',
self.dataItem.baseURL + dataItem.baseURL +
WallpaperUtil.getOnlineWallpaperThumbnailSuffix(), WallpaperUtil.getOnlineWallpaperThumbnailSuffix(),
true); true);
xhr.responseType = 'arraybuffer'; xhr.responseType = 'arraybuffer';
...@@ -148,36 +238,16 @@ cr.define('wallpapers', function() { ...@@ -148,36 +238,16 @@ cr.define('wallpapers', function() {
xhr.addEventListener('load', function(e) { xhr.addEventListener('load', function(e) {
if (xhr.status === 200) { if (xhr.status === 200) {
chrome.wallpaperPrivate.saveThumbnail( chrome.wallpaperPrivate.saveThumbnail(
self.dataItem.baseURL, xhr.response); dataItem.baseURL, xhr.response);
var blob = new Blob( WallpaperUtil.displayImage(
[new Int8Array(xhr.response)], imageElement, xhr.response,
{'type': 'image\/png'}); callback.bind(dataItem.wallpaperId, imageElement));
imageEl.src = window.URL.createObjectURL(blob);
// TODO(bshe): We currently use empty div to reserve space
// for thumbnail. Use a placeholder like "loading" image
// may better.
imageEl.addEventListener('load', function(e) {
self.callback_(
self.dataModelId_, self.dataItem.wallpaperId, this);
window.URL.revokeObjectURL(this.src);
});
} else { } else {
self.callback_(self.dataModelId_); callback();
} }
}); });
} }
}); });
break;
case Constants.WallpaperSourceEnum.Daily:
case Constants.WallpaperSourceEnum.ThirdParty:
default:
// It's impossible to manually select a DAILY or THIRDPARTY type
// wallpaper.
console.error('Unsupported wallpaper source.');
// Delay dispatching the completion callback until all items have
// begun loading and are tracked.
window.setTimeout(this.callback_.bind(this, this.dataModelId_), 0);
}
}, },
}; };
......
...@@ -366,6 +366,30 @@ ...@@ -366,6 +366,30 @@
] ]
} }
] ]
},
{
"name": "getLocalImageData",
"type": "function",
"description": "Read the image data from the file path.",
"nodoc": true,
"parameters": [
{
"type": "string",
"name": "imagePath",
"description": "The path of the image."
},
{
"type": "function",
"name": "callback",
"description": "Function called upon completion.",
"parameters": [
{
"type": "binary",
"name": "imageData"
}
]
}
]
} }
], ],
"events": [ "events": [
......
...@@ -1278,6 +1278,7 @@ enum HistogramValue { ...@@ -1278,6 +1278,7 @@ enum HistogramValue {
WALLPAPERPRIVATE_GETIMAGESINFO, WALLPAPERPRIVATE_GETIMAGESINFO,
ACCESSIBILITY_PRIVATE_SENDSYNTHETICKEYEVENT, ACCESSIBILITY_PRIVATE_SENDSYNTHETICKEYEVENT,
WALLPAPERPRIVATE_GETLOCALIMAGEPATHS, WALLPAPERPRIVATE_GETLOCALIMAGEPATHS,
WALLPAPERPRIVATE_GETLOCALIMAGEDATA,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -14678,6 +14678,7 @@ Called by update_net_error_codes.py.--> ...@@ -14678,6 +14678,7 @@ Called by update_net_error_codes.py.-->
<int value="1215" label="WALLPAPERPRIVATE_GETIMAGESINFO"/> <int value="1215" label="WALLPAPERPRIVATE_GETIMAGESINFO"/>
<int value="1216" label="ACCESSIBILITY_PRIVATE_SENDSYNTHETICKEYEVENT"/> <int value="1216" label="ACCESSIBILITY_PRIVATE_SENDSYNTHETICKEYEVENT"/>
<int value="1217" label="WALLPAPERPRIVATE_GETLOCALIMAGEPATHS"/> <int value="1217" label="WALLPAPERPRIVATE_GETLOCALIMAGEPATHS"/>
<int value="1218" label="WALLPAPERPRIVATE_GETLOCALIMAGEDATA"/>
</enum> </enum>
<enum name="ExtensionIconState"> <enum name="ExtensionIconState">
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