Commit 3195342d authored by Meilin Wang's avatar Meilin Wang Committed by Commit Bot

ambient: Add photo attribution (part 1).

Bug: b/161481195
Test: manually.
Change-Id: Ia86508989c58adb32faef27d74be2910507a382c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2333037
Commit-Queue: Meilin Wang <meilinw@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797142}
parent dd201505
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/ambient/ambient_constants.h" #include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ambient_controller.h" #include "ash/ambient/ambient_controller.h"
#include "ash/ambient/model/ambient_backend_model.h"
#include "ash/public/cpp/ambient/ambient_client.h" #include "ash/public/cpp/ambient/ambient_client.h"
#include "ash/public/cpp/image_downloader.h" #include "ash/public/cpp/image_downloader.h"
#include "ash/shell.h" #include "ash/shell.h"
...@@ -328,9 +329,8 @@ void AmbientPhotoController::OnScreenUpdateInfoFetched( ...@@ -328,9 +329,8 @@ void AmbientPhotoController::OnScreenUpdateInfoFetched(
void AmbientPhotoController::TryReadPhotoRawData() { void AmbientPhotoController::TryReadPhotoRawData() {
const AmbientModeTopic& topic = GetNextTopic(); const AmbientModeTopic& topic = GetNextTopic();
const std::string& image_url = topic.portrait_image_url.value_or(topic.url);
base::FilePath path = photo_path_.Append(ToPhotoFileName(image_url)); base::FilePath path = photo_path_.Append(ToPhotoFileName(topic.GetUrl()));
task_runner_->PostTaskAndReplyWithResult( task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
...@@ -342,25 +342,25 @@ void AmbientPhotoController::TryReadPhotoRawData() { ...@@ -342,25 +342,25 @@ void AmbientPhotoController::TryReadPhotoRawData() {
}, },
path), path),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataRead, base::BindOnce(&AmbientPhotoController::OnPhotoRawDataRead,
weak_factory_.GetWeakPtr(), image_url)); weak_factory_.GetWeakPtr(), topic));
} }
void AmbientPhotoController::OnPhotoRawDataRead( void AmbientPhotoController::OnPhotoRawDataRead(
const std::string& image_url, const AmbientModeTopic& topic,
std::unique_ptr<std::string> data) { std::unique_ptr<std::string> data) {
if (!data || data->empty()) { if (!data || data->empty()) {
url_loader_->Download( url_loader_->Download(
image_url, topic.GetUrl(),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), image_url, weak_factory_.GetWeakPtr(), topic,
/*need_to_save=*/true)); /*need_to_save=*/true));
} else { } else {
OnPhotoRawDataAvailable(image_url, /*need_to_save=*/false, std::move(data)); OnPhotoRawDataAvailable(topic, /*need_to_save=*/false, std::move(data));
} }
} }
void AmbientPhotoController::OnPhotoRawDataAvailable( void AmbientPhotoController::OnPhotoRawDataAvailable(
const std::string& image_url, const AmbientModeTopic& topic,
bool need_to_save, bool need_to_save,
std::unique_ptr<std::string> response_body) { std::unique_ptr<std::string> response_body) {
if (!response_body) { if (!response_body) {
...@@ -377,7 +377,8 @@ void AmbientPhotoController::OnPhotoRawDataAvailable( ...@@ -377,7 +377,8 @@ void AmbientPhotoController::OnPhotoRawDataAvailable(
return; return;
} }
const base::FilePath path = photo_path_.Append(ToPhotoFileName(image_url)); const base::FilePath path =
photo_path_.Append(ToPhotoFileName(topic.GetUrl()));
task_runner_->PostTaskAndReply( task_runner_->PostTaskAndReply(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
...@@ -388,24 +389,30 @@ void AmbientPhotoController::OnPhotoRawDataAvailable( ...@@ -388,24 +389,30 @@ void AmbientPhotoController::OnPhotoRawDataAvailable(
}, },
path, need_to_save, *response_body), path, need_to_save, *response_body),
base::BindOnce(&AmbientPhotoController::DecodePhotoRawData, base::BindOnce(&AmbientPhotoController::DecodePhotoRawData,
weak_factory_.GetWeakPtr(), std::move(response_body))); weak_factory_.GetWeakPtr(), topic,
std::move(response_body)));
} }
void AmbientPhotoController::DecodePhotoRawData( void AmbientPhotoController::DecodePhotoRawData(
const AmbientModeTopic& topic,
std::unique_ptr<std::string> data) { std::unique_ptr<std::string> data) {
std::vector<uint8_t> image_bytes(data->begin(), data->end()); std::vector<uint8_t> image_bytes(data->begin(), data->end());
image_decoder_->Decode(image_bytes, image_decoder_->Decode(image_bytes,
base::BindOnce(&AmbientPhotoController::OnPhotoDecoded, base::BindOnce(&AmbientPhotoController::OnPhotoDecoded,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr(), topic));
} }
void AmbientPhotoController::OnPhotoDecoded(const gfx::ImageSkia& image) { void AmbientPhotoController::OnPhotoDecoded(const AmbientModeTopic& topic,
const gfx::ImageSkia& image) {
base::TimeDelta kDelay; base::TimeDelta kDelay;
if (image.isNull()) { if (image.isNull()) {
LOG(WARNING) << "Image is null"; LOG(WARNING) << "Image is null";
kDelay = base::TimeDelta::FromMilliseconds(100); kDelay = base::TimeDelta::FromMilliseconds(100);
} else { } else {
ambient_backend_model_.AddNextImage(image); PhotoWithDetails detailed_photo;
detailed_photo.photo = image;
detailed_photo.details = topic.details;
ambient_backend_model_.AddNextImage(std::move(detailed_photo));
} }
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
......
...@@ -117,16 +117,18 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -117,16 +117,18 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
// If photo raw data is read successfully, call OnPhotoRawDataAvailable() to // If photo raw data is read successfully, call OnPhotoRawDataAvailable() to
// decode data. Otherwise, download the raw data and save to disk. // decode data. Otherwise, download the raw data and save to disk.
void OnPhotoRawDataRead(const std::string& image_url, void OnPhotoRawDataRead(const AmbientModeTopic& topic,
std::unique_ptr<std::string> data); std::unique_ptr<std::string> data);
void OnPhotoRawDataAvailable(const std::string& image_url, void OnPhotoRawDataAvailable(const AmbientModeTopic& topic,
bool need_to_save, bool need_to_save,
std::unique_ptr<std::string> response_body); std::unique_ptr<std::string> response_body);
void DecodePhotoRawData(std::unique_ptr<std::string> data); void DecodePhotoRawData(const AmbientModeTopic& topic,
std::unique_ptr<std::string> data);
void OnPhotoDecoded(const gfx::ImageSkia& image); void OnPhotoDecoded(const AmbientModeTopic& topic,
const gfx::ImageSkia& image);
void StartDownloadingWeatherConditionIcon( void StartDownloadingWeatherConditionIcon(
const base::Optional<WeatherInfo>& weather_info); const base::Optional<WeatherInfo>& weather_info);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/ambient/ambient_constants.h" #include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ambient_controller.h" #include "ash/ambient/ambient_controller.h"
#include "ash/ambient/model/ambient_backend_model.h"
#include "ash/ambient/test/ambient_ash_test_base.h" #include "ash/ambient/test/ambient_ash_test_base.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h" #include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "ash/shell.h" #include "ash/shell.h"
...@@ -55,46 +56,46 @@ TEST_F(AmbientPhotoControllerTest, ShouldStartToDownloadTopics) { ...@@ -55,46 +56,46 @@ TEST_F(AmbientPhotoControllerTest, ShouldStartToDownloadTopics) {
// Test that image is downloaded when starting screen update. // Test that image is downloaded when starting screen update.
TEST_F(AmbientPhotoControllerTest, ShouldStartToDownloadImages) { TEST_F(AmbientPhotoControllerTest, ShouldStartToDownloadImages) {
auto image = photo_controller()->ambient_backend_model()->GetNextImage(); auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.isNull()); EXPECT_TRUE(image.IsNull());
// Start to refresh images. // Start to refresh images.
photo_controller()->StartScreenUpdate(); photo_controller()->StartScreenUpdate();
task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval); task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval);
image = photo_controller()->ambient_backend_model()->GetNextImage(); image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.isNull()); EXPECT_FALSE(image.IsNull());
// Stop to refresh images. // Stop to refresh images.
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
image = photo_controller()->ambient_backend_model()->GetNextImage(); image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.isNull()); EXPECT_TRUE(image.IsNull());
} }
// Tests that photos are updated periodically when starting screen update. // Tests that photos are updated periodically when starting screen update.
TEST_F(AmbientPhotoControllerTest, ShouldUpdatePhotoPeriodically) { TEST_F(AmbientPhotoControllerTest, ShouldUpdatePhotoPeriodically) {
gfx::ImageSkia image1; PhotoWithDetails image1;
gfx::ImageSkia image2; PhotoWithDetails image2;
gfx::ImageSkia image3; PhotoWithDetails image3;
// Start to refresh images. // Start to refresh images.
photo_controller()->StartScreenUpdate(); photo_controller()->StartScreenUpdate();
task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval); task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval);
image1 = photo_controller()->ambient_backend_model()->GetNextImage(); image1 = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image1.isNull()); EXPECT_FALSE(image1.IsNull());
EXPECT_TRUE(image2.isNull()); EXPECT_TRUE(image2.IsNull());
// Fastforward enough time to update the photo. // Fastforward enough time to update the photo.
task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval); task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval);
image2 = photo_controller()->ambient_backend_model()->GetNextImage(); image2 = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image2.isNull()); EXPECT_FALSE(image2.IsNull());
EXPECT_FALSE(image1.BackedBySameObjectAs(image2)); EXPECT_FALSE(image1.photo.BackedBySameObjectAs(image2.photo));
EXPECT_TRUE(image3.isNull()); EXPECT_TRUE(image3.IsNull());
// Fastforward enough time to update another photo. // Fastforward enough time to update another photo.
task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval); task_environment()->FastForwardBy(1.2 * kPhotoRefreshInterval);
image3 = photo_controller()->ambient_backend_model()->GetNextImage(); image3 = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image3.isNull()); EXPECT_FALSE(image3.IsNull());
EXPECT_FALSE(image1.BackedBySameObjectAs(image3)); EXPECT_FALSE(image1.photo.BackedBySameObjectAs(image3.photo));
EXPECT_FALSE(image2.BackedBySameObjectAs(image3)); EXPECT_FALSE(image2.photo.BackedBySameObjectAs(image3.photo));
// Stop to refresh images. // Stop to refresh images.
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
...@@ -139,7 +140,7 @@ TEST_F(AmbientPhotoControllerTest, ShouldSaveAndDeleteImagesOnDisk) { ...@@ -139,7 +140,7 @@ TEST_F(AmbientPhotoControllerTest, ShouldSaveAndDeleteImagesOnDisk) {
} }
auto image = photo_controller()->ambient_backend_model()->GetNextImage(); auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.isNull()); EXPECT_FALSE(image.IsNull());
// Stop to refresh images. // Stop to refresh images.
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
...@@ -149,7 +150,7 @@ TEST_F(AmbientPhotoControllerTest, ShouldSaveAndDeleteImagesOnDisk) { ...@@ -149,7 +150,7 @@ TEST_F(AmbientPhotoControllerTest, ShouldSaveAndDeleteImagesOnDisk) {
EXPECT_TRUE(base::IsDirectoryEmpty(ambient_image_path)); EXPECT_TRUE(base::IsDirectoryEmpty(ambient_image_path));
image = photo_controller()->ambient_backend_model()->GetNextImage(); image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.isNull()); EXPECT_TRUE(image.IsNull());
} }
} // namespace ash } // namespace ash
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "ash/ambient/backdrop/ambient_backend_controller_impl.h" #include "ash/ambient/backdrop/ambient_backend_controller_impl.h"
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
...@@ -77,29 +78,66 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest( ...@@ -77,29 +78,66 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
return resource_request; return resource_request;
} }
std::string BuildCuratedTopicDetails(
const backdrop::ScreenUpdate::Topic& topic) {
if (topic.has_metadata_line_1() && topic.has_metadata_line_2()) {
// Uses a space as the separator between.
return topic.metadata_line_1() + " " + topic.metadata_line_2();
} else if (topic.has_metadata_line_1()) {
return topic.metadata_line_1();
} else if (topic.has_metadata_line_2()) {
return topic.metadata_line_2();
} else {
return std::string();
}
}
std::string BuildPersonalTopicDetails(
const backdrop::ScreenUpdate::Topic& topic) {
// |metadata_line_1| contains the album name.
return topic.has_metadata_line_1() ? topic.metadata_line_1() : std::string();
}
void BuildBackdropTopicDetails(
const backdrop::ScreenUpdate::Topic& backdrop_topic,
AmbientModeTopic& ambient_topic) {
switch (backdrop_topic.topic_type()) {
case backdrop::TopicSource::CURATED:
ambient_topic.details = BuildCuratedTopicDetails(backdrop_topic);
break;
case backdrop::TopicSource::PERSONAL_PHOTO:
ambient_topic.details = BuildPersonalTopicDetails(backdrop_topic);
break;
default:
ambient_topic.details = std::string();
break;
}
}
// Helper function to save the information we got from the backdrop server to a // Helper function to save the information we got from the backdrop server to a
// public struct so that they can be accessed by public codes. // public struct so that they can be accessed by public codes.
ash::ScreenUpdate ToScreenUpdate( ScreenUpdate ToScreenUpdate(
const backdrop::ScreenUpdate& backdrop_screen_update) { const backdrop::ScreenUpdate& backdrop_screen_update) {
ash::ScreenUpdate screen_update; ScreenUpdate screen_update;
// Parse |AmbientModeTopic|. // Parse |AmbientModeTopic|.
int topics_size = backdrop_screen_update.next_topics_size(); int topics_size = backdrop_screen_update.next_topics_size();
if (topics_size > 0) { if (topics_size > 0) {
for (auto backdrop_topic : backdrop_screen_update.next_topics()) { for (auto& backdrop_topic : backdrop_screen_update.next_topics()) {
ash::AmbientModeTopic topic; AmbientModeTopic ambient_topic;
DCHECK(backdrop_topic.has_url()); DCHECK(backdrop_topic.has_url());
topic.url = backdrop_topic.url(); ambient_topic.url = backdrop_topic.url();
if (backdrop_topic.has_portrait_image_url()) if (backdrop_topic.has_portrait_image_url())
topic.portrait_image_url = backdrop_topic.portrait_image_url(); ambient_topic.portrait_image_url = backdrop_topic.portrait_image_url();
screen_update.next_topics.emplace_back(topic); BuildBackdropTopicDetails(backdrop_topic, ambient_topic);
screen_update.next_topics.emplace_back(ambient_topic);
} }
} }
// Parse |WeatherInfo|. // Parse |WeatherInfo|.
if (backdrop_screen_update.has_weather_info()) { if (backdrop_screen_update.has_weather_info()) {
const auto& backdrop_weather_info = backdrop_screen_update.weather_info(); backdrop::WeatherInfo backdrop_weather_info =
ash::WeatherInfo weather_info; backdrop_screen_update.weather_info();
WeatherInfo weather_info;
if (backdrop_weather_info.has_condition_icon_url()) { if (backdrop_weather_info.has_condition_icon_url()) {
weather_info.condition_icon_url = weather_info.condition_icon_url =
backdrop_weather_info.condition_icon_url(); backdrop_weather_info.condition_icon_url();
......
...@@ -11,6 +11,30 @@ ...@@ -11,6 +11,30 @@
namespace ash { namespace ash {
// PhotoWithDetails------------------------------------------------------------
PhotoWithDetails::PhotoWithDetails() = default;
PhotoWithDetails::PhotoWithDetails(const PhotoWithDetails&) = default;
PhotoWithDetails& PhotoWithDetails::operator=(const PhotoWithDetails&) =
default;
PhotoWithDetails::PhotoWithDetails(PhotoWithDetails&&) = default;
PhotoWithDetails& PhotoWithDetails::operator=(PhotoWithDetails&&) = default;
PhotoWithDetails::~PhotoWithDetails() = default;
void PhotoWithDetails::Clear() {
photo = gfx::ImageSkia();
details = std::string();
}
bool PhotoWithDetails::IsNull() const {
return photo.isNull();
}
// AmbientBackendModel---------------------------------------------------------
AmbientBackendModel::AmbientBackendModel() { AmbientBackendModel::AmbientBackendModel() {
SetPhotoRefreshInterval(kPhotoRefreshInterval); SetPhotoRefreshInterval(kPhotoRefreshInterval);
} }
...@@ -34,17 +58,18 @@ void AmbientBackendModel::AppendTopics( ...@@ -34,17 +58,18 @@ void AmbientBackendModel::AppendTopics(
bool AmbientBackendModel::ShouldFetchImmediately() const { bool AmbientBackendModel::ShouldFetchImmediately() const {
// Prefetch one image |next_image_| for photo transition animation. // Prefetch one image |next_image_| for photo transition animation.
return current_image_.isNull() || next_image_.isNull(); return current_image_.IsNull() || next_image_.IsNull();
} }
void AmbientBackendModel::AddNextImage(const gfx::ImageSkia& image) { void AmbientBackendModel::AddNextImage(
if (current_image_.isNull()) { const PhotoWithDetails& photo_with_details) {
current_image_ = image; if (current_image_.IsNull()) {
} else if (next_image_.isNull()) { current_image_ = photo_with_details;
next_image_ = image; } else if (next_image_.IsNull()) {
next_image_ = photo_with_details;
} else { } else {
current_image_ = next_image_; current_image_ = next_image_;
next_image_ = image; next_image_ = photo_with_details;
} }
NotifyImagesChanged(); NotifyImagesChanged();
...@@ -63,12 +88,12 @@ void AmbientBackendModel::SetPhotoRefreshInterval(base::TimeDelta interval) { ...@@ -63,12 +88,12 @@ void AmbientBackendModel::SetPhotoRefreshInterval(base::TimeDelta interval) {
void AmbientBackendModel::Clear() { void AmbientBackendModel::Clear() {
topics_.clear(); topics_.clear();
current_image_ = gfx::ImageSkia(); current_image_.Clear();
next_image_ = gfx::ImageSkia(); next_image_.Clear();
} }
gfx::ImageSkia AmbientBackendModel::GetNextImage() const { PhotoWithDetails AmbientBackendModel::GetNextImage() const {
if (!next_image_.isNull()) if (!next_image_.IsNull())
return next_image_; return next_image_;
return current_image_; return current_image_;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_ #ifndef ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_
#define ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_ #define ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_
#include <string>
#include <vector> #include <vector>
#include "ash/ash_export.h" #include "ash/ash_export.h"
...@@ -18,6 +19,24 @@ namespace ash { ...@@ -18,6 +19,24 @@ namespace ash {
class AmbientBackendModelObserver; class AmbientBackendModelObserver;
// Contains each photo image and its metadata used to show on ambient.
struct ASH_EXPORT PhotoWithDetails {
PhotoWithDetails();
PhotoWithDetails(const PhotoWithDetails&);
PhotoWithDetails& operator=(const PhotoWithDetails&);
PhotoWithDetails(PhotoWithDetails&&);
PhotoWithDetails& operator=(PhotoWithDetails&&);
~PhotoWithDetails();
void Clear();
bool IsNull() const;
gfx::ImageSkia photo;
std::string details;
};
// Stores necessary information fetched from the backdrop server to render // Stores necessary information fetched from the backdrop server to render
// the photo frame and glanceable weather information on Ambient Mode. Owned // the photo frame and glanceable weather information on Ambient Mode. Owned
// by |AmbientController|. // by |AmbientController|.
...@@ -38,7 +57,7 @@ class ASH_EXPORT AmbientBackendModel { ...@@ -38,7 +57,7 @@ class ASH_EXPORT AmbientBackendModel {
bool ShouldFetchImmediately() const; bool ShouldFetchImmediately() const;
// Add image to local storage. // Add image to local storage.
void AddNextImage(const gfx::ImageSkia& image); void AddNextImage(const PhotoWithDetails& photo);
// Get/Set the photo refresh interval. // Get/Set the photo refresh interval.
base::TimeDelta GetPhotoRefreshInterval(); base::TimeDelta GetPhotoRefreshInterval();
...@@ -48,7 +67,7 @@ class ASH_EXPORT AmbientBackendModel { ...@@ -48,7 +67,7 @@ class ASH_EXPORT AmbientBackendModel {
void Clear(); void Clear();
// Get images from local storage. Could be null image. // Get images from local storage. Could be null image.
gfx::ImageSkia GetNextImage() const; PhotoWithDetails GetNextImage() const;
// Updates the weather information and notifies observers if the icon image is // Updates the weather information and notifies observers if the icon image is
// not null. // not null.
...@@ -80,8 +99,8 @@ class ASH_EXPORT AmbientBackendModel { ...@@ -80,8 +99,8 @@ class ASH_EXPORT AmbientBackendModel {
std::vector<AmbientModeTopic> topics_; std::vector<AmbientModeTopic> topics_;
// Local cache of downloaded images for photo transition animation. // Local cache of downloaded images for photo transition animation.
gfx::ImageSkia current_image_; PhotoWithDetails current_image_;
gfx::ImageSkia next_image_; PhotoWithDetails next_image_;
// The index of currently shown image. // The index of currently shown image.
int current_image_index_ = 0; int current_image_index_ = 0;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "ash/ambient/model/ambient_backend_model.h" #include "ash/ambient/model/ambient_backend_model.h"
#include <memory> #include <memory>
#include <string>
#include "ash/ambient/ambient_constants.h" #include "ash/ambient/ambient_constants.h"
#include "ash/ambient/model/ambient_backend_model_observer.h" #include "ash/ambient/model/ambient_backend_model_observer.h"
...@@ -35,19 +36,24 @@ class AmbientBackendModelTest : public AshTestBase { ...@@ -35,19 +36,24 @@ class AmbientBackendModelTest : public AshTestBase {
// Adds n test images to the model. // Adds n test images to the model.
void AddNTestImages(int n) { void AddNTestImages(int n) {
while (n > 0) { while (n > 0) {
gfx::ImageSkia test_image = PhotoWithDetails test_detailed_image;
test_detailed_image.photo =
gfx::test::CreateImageSkia(/*width=*/10, /*height=*/10); gfx::test::CreateImageSkia(/*width=*/10, /*height=*/10);
ambient_backend_model()->AddNextImage(test_image); test_detailed_image.details = std::string("fake-photo-attribution");
ambient_backend_model()->AddNextImage(std::move(test_detailed_image));
n--; n--;
} }
} }
// Returns whether the image is equivalent to the test image. // Returns whether the image and its details are equivalent to the test
bool EqualsToTestImage(const gfx::ImageSkia& image) { // detailed image.
bool EqualsToTestImage(const PhotoWithDetails& detailed_image) {
gfx::ImageSkia test_image = gfx::ImageSkia test_image =
gfx::test::CreateImageSkia(/*width=*/10, /*height=*/10); gfx::test::CreateImageSkia(/*width=*/10, /*height=*/10);
return !image.isNull() && return !detailed_image.IsNull() &&
gfx::test::AreBitmapsEqual(*image.bitmap(), *test_image.bitmap()); gfx::test::AreBitmapsEqual(*(detailed_image.photo).bitmap(),
*test_image.bitmap()) &&
(detailed_image.details == std::string("fake-photo-attribution"));
} }
// Returns whether the image is null. // Returns whether the image is null.
...@@ -65,7 +71,7 @@ class AmbientBackendModelTest : public AshTestBase { ...@@ -65,7 +71,7 @@ class AmbientBackendModelTest : public AshTestBase {
return ambient_backend_model_.get(); return ambient_backend_model_.get();
} }
gfx::ImageSkia GetNextImage() { PhotoWithDetails GetNextImage() {
return ambient_backend_model_->GetNextImage(); return ambient_backend_model_->GetNextImage();
} }
......
...@@ -118,7 +118,7 @@ void PhotoView::Init() { ...@@ -118,7 +118,7 @@ void PhotoView::Init() {
void PhotoView::UpdateImages() { void PhotoView::UpdateImages() {
auto* model = delegate_->GetAmbientBackendModel(); auto* model = delegate_->GetAmbientBackendModel();
images_unscaled_[image_index_] = model->GetNextImage(); images_unscaled_[image_index_] = model->GetNextImage().photo;
if (images_unscaled_[image_index_].isNull()) if (images_unscaled_[image_index_].isNull())
return; return;
......
...@@ -31,6 +31,10 @@ AmbientModeTopic& AmbientModeTopic::operator=(const AmbientModeTopic&) = ...@@ -31,6 +31,10 @@ AmbientModeTopic& AmbientModeTopic::operator=(const AmbientModeTopic&) =
AmbientModeTopic::~AmbientModeTopic() = default; AmbientModeTopic::~AmbientModeTopic() = default;
std::string AmbientModeTopic::GetUrl() const {
return portrait_image_url.value_or(url);
}
// WeatherInfo------------------------------------------------------------------ // WeatherInfo------------------------------------------------------------------
WeatherInfo::WeatherInfo() = default; WeatherInfo::WeatherInfo() = default;
......
...@@ -27,6 +27,13 @@ struct ASH_PUBLIC_EXPORT AmbientModeTopic { ...@@ -27,6 +27,13 @@ struct ASH_PUBLIC_EXPORT AmbientModeTopic {
AmbientModeTopic& operator=(const AmbientModeTopic&); AmbientModeTopic& operator=(const AmbientModeTopic&);
~AmbientModeTopic(); ~AmbientModeTopic();
// Returns a non-empty url to load the landscape or portrait image.
std::string GetUrl() const;
// Details, i.e. the attribution, to be displayed for the current photo on
// ambient.
std::string details;
// Image url. // Image url.
std::string url; std::string url;
......
...@@ -22,6 +22,8 @@ constexpr AmbientModeTemperatureUnit kTemperatureUnit = ...@@ -22,6 +22,8 @@ constexpr AmbientModeTemperatureUnit kTemperatureUnit =
constexpr char kFakeUrl[] = "chrome://ambient"; constexpr char kFakeUrl[] = "chrome://ambient";
constexpr char kFakeDetails[] = "fake-photo-attribution";
AmbientSettings CreateFakeSettings() { AmbientSettings CreateFakeSettings() {
AmbientSettings settings; AmbientSettings settings;
settings.topic_source = kTopicSource; settings.topic_source = kTopicSource;
...@@ -68,6 +70,7 @@ void FakeAmbientBackendControllerImpl::FetchScreenUpdateInfo( ...@@ -68,6 +70,7 @@ void FakeAmbientBackendControllerImpl::FetchScreenUpdateInfo(
OnScreenUpdateInfoFetchedCallback callback) { OnScreenUpdateInfoFetchedCallback callback) {
ash::AmbientModeTopic topic; ash::AmbientModeTopic topic;
topic.url = kFakeUrl; topic.url = kFakeUrl;
topic.details = kFakeDetails;
ash::WeatherInfo weather_info; ash::WeatherInfo weather_info;
weather_info.temp_f = .0f; weather_info.temp_f = .0f;
......
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