// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/customization/customization_wallpaper_util.h"
#include "ash/public/cpp/wallpaper/wallpaper_controller.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/customization/customization_document.h"
#include "chrome/browser/ash/login/users/avatar/user_image_loader.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_image/user_image.h"
#include "content/public/browser/browser_thread.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "url/gurl.h"
namespace ash {
namespace {
// File path suffixes of resized wallpapers.
constexpr char kSmallWallpaperSuffix[] = "_small";
constexpr char kLargeWallpaperSuffix[] = "_large";
// Returns true if saving the resized |image| to |file_path| succeeded.
bool SaveResizedWallpaper(const gfx::ImageSkia& image,
const gfx::Size& size,
const base::FilePath& file_path) {
gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::RESIZE_LANCZOS3, size);
scoped_refptr<base::RefCountedBytes> image_data = new base::RefCountedBytes();
gfx::JPEGCodec::Encode(*resized_image.bitmap(), 90 /*quality=*/,
&image_data->as_vector());
return base::WriteFile(file_path, *image_data);
}
// Returns true if both file paths exist.
bool CheckCustomizedWallpaperFilesExist(
const base::FilePath& resized_small_path,
const base::FilePath& resized_large_path) {
return base::PathExists(resized_small_path) &&
base::PathExists(resized_large_path);
}
// Resizes and saves the customized default wallpapers.
bool ResizeAndSaveCustomizedDefaultWallpaper(
gfx::ImageSkia image,
const base::FilePath& resized_small_path,
const base::FilePath& resized_large_path) {
return SaveResizedWallpaper(
image,
gfx::Size(kSmallWallpaperMaxWidth, kSmallWallpaperMaxHeight),
resized_small_path) &&
SaveResizedWallpaper(
image,
gfx::Size(kLargeWallpaperMaxWidth, kLargeWallpaperMaxHeight),
resized_large_path);
}
// Checks the result of |ResizeAndSaveCustomizedDefaultWallpaper| and sends
// the paths to apply the wallpapers.
void OnCustomizedDefaultWallpaperResizedAndSaved(
const GURL& wallpaper_url,
const base::FilePath& resized_small_path,
const base::FilePath& resized_large_path,
bool success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!success) {
LOG(WARNING) << "Failed to save resized customized default wallpaper";
return;
}
g_browser_process->local_state()->SetString(
prefs::kCustomizationDefaultWallpaperURL, wallpaper_url.spec());
ash::WallpaperController::Get()->SetCustomizedDefaultWallpaperPaths(
resized_small_path, resized_large_path);
VLOG(1) << "Customized default wallpaper applied.";
}
// Initiates resizing and saving the customized default wallpapers if decoding
// is successful.
void OnCustomizedDefaultWallpaperDecoded(
const GURL& wallpaper_url,
const base::FilePath& resized_small_path,
const base::FilePath& resized_large_path,
std::unique_ptr<user_manager::UserImage> wallpaper) {
// Empty image indicates decode failure.
if (wallpaper->image().isNull()) {
LOG(WARNING) << "Failed to decode customized wallpaper.";
return;
}
wallpaper->image().EnsureRepsForSupportedScales();
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
task_runner->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ResizeAndSaveCustomizedDefaultWallpaper,
wallpaper->image().DeepCopy(), resized_small_path,
resized_large_path),
base::BindOnce(&OnCustomizedDefaultWallpaperResizedAndSaved,
wallpaper_url, resized_small_path, resized_large_path));
}
// If |both_sizes_exist| is false or the url doesn't match the current value,
// initiates image decoding, otherwise directly sends the paths.
void SetCustomizedDefaultWallpaperAfterCheck(
const GURL& wallpaper_url,
const base::FilePath& file_path,
const base::FilePath& resized_small_path,
const base::FilePath& resized_large_path,
bool both_sizes_exist) {
const std::string current_url = g_browser_process->local_state()->GetString(
prefs::kCustomizationDefaultWallpaperURL);
if (both_sizes_exist && current_url == wallpaper_url.spec()) {
ash::WallpaperController::Get()->SetCustomizedDefaultWallpaperPaths(
resized_small_path, resized_small_path);
} else {
// Either resized images do not exist or cached version is incorrect.
// Need to start decoding again.
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
user_image_loader::StartWithFilePath(
task_runner, file_path, ImageDecoder::DEFAULT_CODEC,
0, // Do not crop.
base::BindOnce(&OnCustomizedDefaultWallpaperDecoded, wallpaper_url,
resized_small_path, resized_large_path));
}
}
} // namespace
namespace customization_wallpaper_util {
void StartSettingCustomizedDefaultWallpaper(const GURL& wallpaper_url,
const base::FilePath& file_path) {
// Should fail if this ever happens in tests.
DCHECK(wallpaper_url.is_valid());
if (!wallpaper_url.is_valid()) {
if (!wallpaper_url.is_empty()) {
LOG(WARNING) << "Invalid Customized Wallpaper URL '"
<< wallpaper_url.spec() << "'";
}
return;
}
std::string downloaded_file_name = file_path.BaseName().value();
base::FilePath resized_small_path;
base::FilePath resized_large_path;
if (!GetCustomizedDefaultWallpaperPaths(&resized_small_path,
&resized_large_path)) {
return;
}
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
task_runner->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&CheckCustomizedWallpaperFilesExist, resized_small_path,
resized_large_path),
base::BindOnce(&SetCustomizedDefaultWallpaperAfterCheck, wallpaper_url,
file_path, resized_small_path, resized_large_path));
}
bool GetCustomizedDefaultWallpaperPaths(base::FilePath* small_path_out,
base::FilePath* large_path_out) {
const base::FilePath default_downloaded_file_name =
ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
const base::FilePath default_cache_dir =
ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
if (default_downloaded_file_name.empty() || default_cache_dir.empty()) {
LOG(ERROR) << "Unable to get customized default wallpaper paths.";
return false;
}
const std::string file_name = default_downloaded_file_name.BaseName().value();
*small_path_out = default_cache_dir.Append(file_name + kSmallWallpaperSuffix);
*large_path_out = default_cache_dir.Append(file_name + kLargeWallpaperSuffix);
return true;
}
bool ShouldUseCustomizedDefaultWallpaper() {
PrefService* pref_service = g_browser_process->local_state();
return !pref_service->FindPreference(prefs::kCustomizationDefaultWallpaperURL)
->IsDefaultValue();
}
} // namespace customization_wallpaper_util
} // namespace ash