| 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..6b7659acd7d0d84e1a91a39d2d114d28bc322e41
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/app_list/arc_app_icon.cc
|
| @@ -0,0 +1,246 @@
|
| +// 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"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/files/file_path.h"
|
| +#include "base/files/file_util.h"
|
| +#include "chrome/browser/image_decoder.h"
|
| +#include "chrome/browser/ui/app_list/arc_app_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::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) {
|
| + ArcAppPrefs* prefs = ArcAppPrefs::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::Closure task = base::Bind(&ArcAppIcon::ReadOnFileThread,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + scale_factor,
|
| + path);
|
| + content::BrowserThread::GetBlockingPool()->PostTask(FROM_HERE, task);
|
| +}
|
| +
|
| +void ArcAppIcon::RequestIcon(ui::ScaleFactor scale_factor) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + ArcAppPrefs* prefs = ArcAppPrefs::Get(context_);
|
| + if (!prefs) {
|
| + LOG(ERROR) << "ARC preferences service is not available.";
|
| + return;
|
| + }
|
| + // ArcAppPrefs 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);
|
| +}
|
| +
|
| +void ArcAppIcon::ReadOnFileThread(ui::ScaleFactor scale_factor,
|
| + const base::FilePath& path) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(!path.empty());
|
| +
|
| + if (!base::PathExists(path)) {
|
| + base::Closure task = base::Bind(&ArcAppIcon::RequestIcon,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + scale_factor);
|
| + content::BrowserThread::PostTask(content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + task);
|
| + return;
|
| + }
|
| +
|
| + std::string unsafe_icon_data;
|
| + // Read the file from disk.
|
| + if (!base::ReadFileToString(path, &unsafe_icon_data)) {
|
| + LOG(ERROR) << "Failed to read an ARC icon from file "
|
| + << path.MaybeAsASCII();
|
| + return;
|
| + }
|
| +
|
| + base::Closure task = base::Bind(&ArcAppIcon::OnIconRead,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + scale_factor,
|
| + unsafe_icon_data);
|
| + content::BrowserThread::PostTask(content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + task);
|
| +}
|
| +
|
| +void ArcAppIcon::OnIconRead(ui::ScaleFactor scale_factor,
|
| + const std::string& unsafe_image_data) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + decode_requests_.push_back(new DecodeRequest(weak_ptr_factory_.GetWeakPtr(),
|
| + resource_size_in_dip_,
|
| + scale_factor));
|
| + ImageDecoder::Start(decode_requests_.back(), unsafe_image_data);
|
| +}
|
| +
|
| +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);
|
| +}
|
|
|