Chromium Code Reviews| Index: chrome/browser/ui/app_list/arc_app_icon.cc |
| diff --git a/chrome/browser/ui/app_list/arc_app_icon.cc b/chrome/browser/ui/app_list/arc_app_icon.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4308257c0c9f401a56ee9ee89a5a091af0447593 |
| --- /dev/null |
| +++ b/chrome/browser/ui/app_list/arc_app_icon.cc |
| @@ -0,0 +1,269 @@ |
| +// Copyright 2015 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. |
| + |
| +#include "chrome/browser/ui/app_list/arc_app_icon.h" |
|
Matt Giuca
2015/11/19 09:12:36
This is a significant number of files. I think it
khmel1
2015/11/19 17:11:42
Done.
|
| + |
| +#include <algorithm> |
| + |
| +#include "base/bind.h" |
| +#include "base/files/file_path.h" |
| +#include "base/files/file_util.h" |
| +#include "base/task_runner_util.h" |
| +#include "chrome/browser/image_decoder.h" |
| +#include "chrome/browser/ui/app_list/arc_app_list_prefs.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "extensions/grit/extensions_browser_resources.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/gfx/geometry/size.h" |
| +#include "ui/gfx/image/image_skia_source.h" |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// ArcAppIcon::ReadResult |
| + |
| +struct ArcAppIcon::ReadResult { |
| + enum Status { |
| + OK, |
| + FAIL, |
| + REQUEST_TO_INSTALL, |
| + }; |
| + |
| + Status status; |
| + ui::ScaleFactor scale_factor; |
| + std::string unsafe_icon_data; |
| +}; |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// ArcAppIcon::Source |
| + |
| +class ArcAppIcon::Source : public gfx::ImageSkiaSource { |
| + public: |
| + explicit Source(const base::WeakPtr<ArcAppIcon>& host); |
| + ~Source() override; |
| + |
| + private: |
| + // gfx::ImageSkiaSource overrides: |
| + gfx::ImageSkiaRep GetImageForScale(float scale) override; |
| + |
| + // Used to load images asynchronously. NULLed out when the ArcAppIcon is |
| + // destroyed. |
| + base::WeakPtr<ArcAppIcon> host_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Source); |
| +}; |
| + |
| +ArcAppIcon::Source::Source(const base::WeakPtr<ArcAppIcon>& host) |
| + : host_(host) { |
| +} |
| + |
| +ArcAppIcon::Source::~Source() { |
| +} |
| + |
| +gfx::ImageSkiaRep ArcAppIcon::Source::GetImageForScale(float scale) { |
| + if (host_) { |
| + host_->LoadForScaleFactor(ui::GetSupportedScaleFactor(scale)); |
| + } |
| + |
| + // Host loads icon asynchronously, so use default icon so far. |
| + const gfx::ImageSkia* default_image = ResourceBundle::GetSharedInstance(). |
| + GetImageSkiaNamed(IDR_APP_DEFAULT_ICON); |
| + CHECK(default_image); |
| + |
| + return default_image->GetRepresentation(scale); |
| +} |
| + |
| +class ArcAppIcon::DecodeRequest : public ImageDecoder::ImageRequest { |
| + public: |
| + DecodeRequest(const base::WeakPtr<ArcAppIcon>& host, |
| + int dimension, |
| + ui::ScaleFactor scale_factor); |
| + ~DecodeRequest() override; |
| + |
| + // ImageDecoder::ImageRequest |
| + void OnImageDecoded(const SkBitmap& bitmap) override; |
| + void OnDecodeImageFailed() override; |
| + private: |
| + base::WeakPtr<ArcAppIcon> host_; |
| + int dimension_; |
| + ui::ScaleFactor scale_factor_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DecodeRequest); |
| +}; |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// ArcAppIcon::DecodeRequest |
| + |
| +ArcAppIcon::DecodeRequest::DecodeRequest(const base::WeakPtr<ArcAppIcon>& host, |
| + int dimension, |
| + ui::ScaleFactor scale_factor) |
| + : host_(host), |
| + dimension_(dimension), |
| + scale_factor_(scale_factor) { |
| +} |
| + |
| +ArcAppIcon::DecodeRequest::~DecodeRequest() { |
| +} |
| + |
| +void ArcAppIcon::DecodeRequest::OnImageDecoded(const SkBitmap& bitmap) { |
| + DCHECK(!bitmap.isNull() && !bitmap.empty()); |
| + |
| + if (!host_) { |
| + return; |
| + } |
| + |
| + int expected_dim = static_cast<int>( |
| + ui::GetScaleForScaleFactor(scale_factor_) * dimension_ + 0.5f); |
| + if (bitmap.width() != expected_dim || bitmap.height() != expected_dim) { |
| + LOG(ERROR) << "Decoded ARC icon has unexpected dimension " |
| + << bitmap.width() << "x" << bitmap.height() << ". Expected " |
| + << expected_dim << "x" << "."; |
| + return; |
| + } |
| + |
| + gfx::ImageSkia image_skia; |
| + image_skia.AddRepresentation(gfx::ImageSkiaRep( |
| + bitmap, |
| + ui::GetScaleForScaleFactor(scale_factor_))); |
| + |
| + host_->Update(&image_skia); |
| + host_->DiscardDecodeRequest(this); |
| +} |
| + |
| +void ArcAppIcon::DecodeRequest::OnDecodeImageFailed() { |
| + LOG(ERROR) << "Failed to decode ARC icon."; |
| + |
| + if (!host_) { |
| + return; |
| + } |
| + |
| + host_->DiscardDecodeRequest(this); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// ArcAppIcon |
| + |
| +ArcAppIcon::ArcAppIcon(content::BrowserContext* context, |
| + const std::string& app_id, |
| + int resource_size_in_dip, |
| + Observer* observer) |
| + : context_(context), |
| + app_id_(app_id), |
| + resource_size_in_dip_(resource_size_in_dip), |
| + observer_(observer), |
| + weak_ptr_factory_(this) { |
| + CHECK(observer_ != NULL); |
| + source_ = new Source(weak_ptr_factory_.GetWeakPtr()); |
| + gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip); |
| + image_skia_ = gfx::ImageSkia(source_, resource_size); |
| +} |
| + |
| +ArcAppIcon::~ArcAppIcon() { |
| +} |
| + |
| +void ArcAppIcon::LoadForScaleFactor(ui::ScaleFactor scale_factor) { |
| + ArcAppListPrefs* prefs = ArcAppListPrefs::Get(context_); |
| + if (!prefs) { |
| + LOG(ERROR) << "ARC preferences service is not available."; |
| + return; |
| + } |
| + |
| + base::FilePath path = prefs->GetIconPath(app_id_, scale_factor); |
| + if (path.empty()) { |
| + return; |
| + } |
| + |
| + base::PostTaskAndReplyWithResult(content::BrowserThread::GetBlockingPool(), |
| + FROM_HERE, |
| + base::Bind(&ArcAppIcon::ReadOnFileThread, |
| + scale_factor, |
| + path), |
| + base::Bind(&ArcAppIcon::OnIconRead, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +void ArcAppIcon::RequestIcon(ui::ScaleFactor scale_factor) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + ArcAppListPrefs* prefs = ArcAppListPrefs::Get(context_); |
| + if (!prefs) { |
| + LOG(ERROR) << "ARC preferences service is not available."; |
| + return; |
| + } |
| + // ArcAppListPrefs notifies ArcAppModelBuilder via Observer when icon is ready |
| + // and ArcAppModelBuilder refreshes the icon of the corresponding item by |
| + // calling LoadScaleFactor. |
| + prefs->RequestIcon(app_id_, scale_factor); |
| +} |
| + |
| +// static |
| +scoped_ptr<ArcAppIcon::ReadResult> ArcAppIcon::ReadOnFileThread( |
| + ui::ScaleFactor scale_factor, |
| + const base::FilePath& path) { |
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| + DCHECK(!path.empty()); |
| + |
| + scoped_ptr<ArcAppIcon::ReadResult> result(new ArcAppIcon::ReadResult()); |
| + result->scale_factor = scale_factor; |
| + |
| + if (!base::PathExists(path)) { |
| + result->status = ArcAppIcon::ReadResult::REQUEST_TO_INSTALL; |
| + return result.Pass(); |
| + } |
| + |
| + // Read the file from disk. |
| + if (!base::ReadFileToString(path, &result->unsafe_icon_data)) { |
| + LOG(ERROR) << "Failed to read an ARC icon from file " |
| + << path.MaybeAsASCII(); |
| + result->status = ArcAppIcon::ReadResult::FAIL; |
| + return result.Pass(); |
| + } |
| + |
| + result->status = ArcAppIcon::ReadResult::OK; |
| + return result.Pass(); |
| +} |
| + |
| +void ArcAppIcon::OnIconRead(scoped_ptr<ArcAppIcon::ReadResult> read_result) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + |
| + switch (read_result->status) { |
| + case ReadResult::OK: |
| + decode_requests_.push_back(new DecodeRequest(weak_ptr_factory_.GetWeakPtr(), |
| + resource_size_in_dip_, |
| + read_result->scale_factor)); |
| + ImageDecoder::Start(decode_requests_.back(), read_result->unsafe_icon_data); |
| + break; |
| + case ReadResult::FAIL: |
| + break; |
| + case ReadResult::REQUEST_TO_INSTALL: |
| + RequestIcon(read_result->scale_factor); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| +} |
| + |
| +void ArcAppIcon::Update(const gfx::ImageSkia* image) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + CHECK(image && !image->isNull()); |
| + |
| + std::vector<gfx::ImageSkiaRep> reps = image->image_reps(); |
| + for (const auto& image_rep : reps) { |
| + if (ui::IsSupportedScale(image_rep.scale())) { |
| + image_skia_.RemoveRepresentation(image_rep.scale()); |
| + image_skia_.AddRepresentation(image_rep); |
| + } |
| + } |
| + |
| + image_ = gfx::Image(image_skia_); |
| + |
| + observer_->OnIconUpdated(); |
| +} |
| + |
| +void ArcAppIcon::DiscardDecodeRequest(DecodeRequest* request) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + |
| + ScopedVector<DecodeRequest>::iterator it = std::find(decode_requests_.begin(), |
| + decode_requests_.end(), |
| + request); |
| + CHECK(it != decode_requests_.end()); |
| + decode_requests_.erase(it); |
| +} |