Commit e8d58900 authored by wutao's avatar wutao Committed by Commit Bot

ambient: Tile related photos in landscape

This patch shows two related photos together when device is in landscape
orientation.

Major changes:
1. Only show paired photos in landscape mode.
2. Use flex layout to dynamically add and show the secondary image.
3. If the primary image is fetched, it will be shown in the UI.
4. If the primary image is not fetched, then will ignore the secondary
   image, i.e. read from cache.
5. If the second image is fetched, it will be added to the UI if the
   screen orientation is landscape.
6. There is a 8px wide separator between two photos.
7. Only update the cache with primary image.
8. Paired photos share the same attributes.

Screenshot: https://screenshot.googleplex.com/9UirWBGxheYVqV4

Bug: b/157080996
Test: added new tests.
Change-Id: I25fb18104f317ffad629bbf7d1fcc5302806cf20
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2407176
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809702}
parent 4c5c54a6
...@@ -48,6 +48,10 @@ constexpr char kAmbientModeDirectoryName[] = "ambient-mode"; ...@@ -48,6 +48,10 @@ constexpr char kAmbientModeDirectoryName[] = "ambient-mode";
constexpr base::TimeDelta kTokenUsageTimeBuffer = constexpr base::TimeDelta kTokenUsageTimeBuffer =
base::TimeDelta::FromMinutes(10); base::TimeDelta::FromMinutes(10);
// PhotoView related constants.
// Spacing between two portrait images.
constexpr int kMarginLeftOfRelatedImageDip = 8;
} // namespace ash } // namespace ash
#endif // ASH_AMBIENT_AMBIENT_CONSTANTS_H_ #endif // ASH_AMBIENT_AMBIENT_CONSTANTS_H_
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#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"
#include "base/barrier_closure.h"
#include "base/base64.h" #include "base/base64.h"
#include "base/base_paths.h" #include "base/base_paths.h"
#include "base/bind.h" #include "base/bind.h"
...@@ -324,15 +325,44 @@ void AmbientPhotoController::OnScreenUpdateInfoFetched( ...@@ -324,15 +325,44 @@ void AmbientPhotoController::OnScreenUpdateInfoFetched(
StartDownloadingWeatherConditionIcon(screen_update.weather_info); StartDownloadingWeatherConditionIcon(screen_update.weather_info);
} }
void AmbientPhotoController::ResetImageData() {
image_data_.reset();
related_image_data_.reset();
image_details_.reset();
image_ = gfx::ImageSkia();
related_image_ = gfx::ImageSkia();
}
void AmbientPhotoController::FetchPhotoRawData() { void AmbientPhotoController::FetchPhotoRawData() {
const AmbientModeTopic* topic = GetNextTopic(); const AmbientModeTopic* topic = GetNextTopic();
ResetImageData();
if (topic) { if (topic) {
const int num_callbacks = (topic->related_image_url) ? 2 : 1;
auto on_done = base::BarrierClosure(
num_callbacks,
base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(),
/*from_downloading=*/true));
url_loader_->Download( url_loader_->Download(
topic->GetUrl(), topic->url,
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
/*from_downloading=*/true, /*from_downloading=*/true,
/*is_related_image=*/false, on_done,
std::make_unique<std::string>(topic->details))); std::make_unique<std::string>(topic->details)));
if (topic->related_image_url) {
url_loader_->Download(
*(topic->related_image_url),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(),
/*from_downloading=*/true,
/*is_related_image=*/true, on_done,
std::make_unique<std::string>(topic->details)));
}
return; return;
} }
...@@ -367,6 +397,10 @@ void AmbientPhotoController::TryReadPhotoRawData() { ...@@ -367,6 +397,10 @@ void AmbientPhotoController::TryReadPhotoRawData() {
auto photo_data = std::make_unique<std::string>(); auto photo_data = std::make_unique<std::string>();
auto photo_details = std::make_unique<std::string>(); auto photo_details = std::make_unique<std::string>();
auto on_done =
base::BindRepeating(&AmbientPhotoController::OnAllPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(),
/*from_downloading=*/false);
task_runner_->PostTaskAndReply( task_runner_->PostTaskAndReply(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
...@@ -386,14 +420,27 @@ void AmbientPhotoController::TryReadPhotoRawData() { ...@@ -386,14 +420,27 @@ void AmbientPhotoController::TryReadPhotoRawData() {
file_name, photo_data.get(), photo_details.get()), file_name, photo_data.get(), photo_details.get()),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), /*from_downloading=*/false, weak_factory_.GetWeakPtr(), /*from_downloading=*/false,
/*is_related_image=*/false, on_done,
std::move(photo_details), std::move(photo_data))); std::move(photo_details), std::move(photo_data)));
} }
void AmbientPhotoController::OnPhotoRawDataAvailable( void AmbientPhotoController::OnPhotoRawDataAvailable(
bool from_downloading, bool from_downloading,
bool is_related_image,
base::RepeatingClosure on_done,
std::unique_ptr<std::string> details, std::unique_ptr<std::string> details,
std::unique_ptr<std::string> data) { std::unique_ptr<std::string> data) {
if (!data || data->empty()) { if (is_related_image)
related_image_data_ = std::move(data);
else {
image_data_ = std::move(data);
image_details_ = std::move(details);
}
std::move(on_done).Run();
}
void AmbientPhotoController::OnAllPhotoRawDataAvailable(bool from_downloading) {
if (!image_data_ || image_data_->empty()) {
if (from_downloading) { if (from_downloading) {
LOG(ERROR) << "Failed to download image"; LOG(ERROR) << "Failed to download image";
resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false); resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false);
...@@ -414,6 +461,12 @@ void AmbientPhotoController::OnPhotoRawDataAvailable( ...@@ -414,6 +461,12 @@ void AmbientPhotoController::OnPhotoRawDataAvailable(
if (cache_index_for_store_ == kMaxNumberOfCachedImages) if (cache_index_for_store_ == kMaxNumberOfCachedImages)
cache_index_for_store_ = 0; cache_index_for_store_ = 0;
const int num_callbacks = related_image_data_ ? 2 : 1;
auto on_done = base::BarrierClosure(
num_callbacks,
base::BindOnce(&AmbientPhotoController::OnAllPhotoDecoded,
weak_factory_.GetWeakPtr(), from_downloading));
task_runner_->PostTaskAndReply( task_runner_->PostTaskAndReply(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
...@@ -425,28 +478,44 @@ void AmbientPhotoController::OnPhotoRawDataAvailable( ...@@ -425,28 +478,44 @@ void AmbientPhotoController::OnPhotoRawDataAvailable(
details); details);
} }
}, },
file_name, from_downloading, *data, *details), file_name, from_downloading, *image_data_, *image_details_),
base::BindOnce(&AmbientPhotoController::DecodePhotoRawData, base::BindOnce(&AmbientPhotoController::DecodePhotoRawData,
weak_factory_.GetWeakPtr(), from_downloading, weak_factory_.GetWeakPtr(), from_downloading,
std::move(details), std::move(data))); /*is_related_image=*/false, on_done,
std::move(image_data_)));
if (related_image_data_) {
DecodePhotoRawData(from_downloading, /*is_related_image=*/true, on_done,
std::move(related_image_data_));
}
} }
void AmbientPhotoController::DecodePhotoRawData( void AmbientPhotoController::DecodePhotoRawData(
bool from_downloading, bool from_downloading,
std::unique_ptr<std::string> details, bool is_related_image,
base::RepeatingClosure on_done,
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_decoder_->Decode(
image_bytes, base::BindOnce(&AmbientPhotoController::OnPhotoDecoded, image_bytes, base::BindOnce(&AmbientPhotoController::OnPhotoDecoded,
weak_factory_.GetWeakPtr(), from_downloading, weak_factory_.GetWeakPtr(), from_downloading,
std::move(details))); is_related_image, on_done));
} }
void AmbientPhotoController::OnPhotoDecoded( void AmbientPhotoController::OnPhotoDecoded(bool from_downloading,
bool from_downloading, bool is_related_image,
std::unique_ptr<std::string> details, base::RepeatingClosure on_done,
const gfx::ImageSkia& image) { const gfx::ImageSkia& image) {
if (image.isNull()) { if (is_related_image)
related_image_ = image;
else
image_ = image;
std::move(on_done).Run();
}
void AmbientPhotoController::OnAllPhotoDecoded(bool from_downloading) {
if (image_.isNull()) {
LOG(WARNING) << "Image is null"; LOG(WARNING) << "Image is null";
if (from_downloading) if (from_downloading)
resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false); resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false);
...@@ -461,8 +530,12 @@ void AmbientPhotoController::OnPhotoDecoded( ...@@ -461,8 +530,12 @@ void AmbientPhotoController::OnPhotoDecoded(
resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/true); resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/true);
PhotoWithDetails detailed_photo; PhotoWithDetails detailed_photo;
detailed_photo.photo = image; detailed_photo.photo = image_;
detailed_photo.details = *details; detailed_photo.related_photo = related_image_;
detailed_photo.details = *image_details_;
ResetImageData();
ambient_backend_model_.AddNextImage(std::move(detailed_photo)); ambient_backend_model_.AddNextImage(std::move(detailed_photo));
ScheduleRefreshImage(); ScheduleRefreshImage();
......
...@@ -118,6 +118,9 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -118,6 +118,9 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
void OnScreenUpdateInfoFetched(const ash::ScreenUpdate& screen_update); void OnScreenUpdateInfoFetched(const ash::ScreenUpdate& screen_update);
// Clear temporary image data to prepare next photos.
void ResetImageData();
// Fetch photo raw data by downloading or reading from cache. // Fetch photo raw data by downloading or reading from cache.
void FetchPhotoRawData(); void FetchPhotoRawData();
...@@ -125,17 +128,25 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -125,17 +128,25 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
void TryReadPhotoRawData(); void TryReadPhotoRawData();
void OnPhotoRawDataAvailable(bool from_downloading, void OnPhotoRawDataAvailable(bool from_downloading,
bool is_related_image,
base::RepeatingClosure on_done,
std::unique_ptr<std::string> details, std::unique_ptr<std::string> details,
std::unique_ptr<std::string> data); std::unique_ptr<std::string> data);
void OnAllPhotoRawDataAvailable(bool from_downloading);
void DecodePhotoRawData(bool from_downloading, void DecodePhotoRawData(bool from_downloading,
std::unique_ptr<std::string> details, bool is_related_image,
base::RepeatingClosure on_done,
std::unique_ptr<std::string> data); std::unique_ptr<std::string> data);
void OnPhotoDecoded(bool from_downloading, void OnPhotoDecoded(bool from_downloading,
std::unique_ptr<std::string> details, bool is_related_image,
base::RepeatingClosure on_done,
const gfx::ImageSkia& image); const gfx::ImageSkia& image);
void OnAllPhotoDecoded(bool from_downloading);
void StartDownloadingWeatherConditionIcon( void StartDownloadingWeatherConditionIcon(
const base::Optional<WeatherInfo>& weather_info); const base::Optional<WeatherInfo>& weather_info);
...@@ -209,6 +220,13 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -209,6 +220,13 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Temporary data store when fetching images and details.
std::unique_ptr<std::string> image_data_;
std::unique_ptr<std::string> related_image_data_;
std::unique_ptr<std::string> image_details_;
gfx::ImageSkia image_;
gfx::ImageSkia related_image_;
base::WeakPtrFactory<AmbientPhotoController> weak_factory_{this}; base::WeakPtrFactory<AmbientPhotoController> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(AmbientPhotoController); DISALLOW_COPY_AND_ASSIGN(AmbientPhotoController);
......
...@@ -127,9 +127,22 @@ ScreenUpdate ToScreenUpdate( ...@@ -127,9 +127,22 @@ ScreenUpdate ToScreenUpdate(
for (auto& backdrop_topic : backdrop_screen_update.next_topics()) { for (auto& backdrop_topic : backdrop_screen_update.next_topics()) {
AmbientModeTopic ambient_topic; AmbientModeTopic ambient_topic;
DCHECK(backdrop_topic.has_url()); DCHECK(backdrop_topic.has_url());
ambient_topic.url = backdrop_topic.url();
if (backdrop_topic.has_portrait_image_url()) if (backdrop_topic.has_portrait_image_url())
ambient_topic.portrait_image_url = backdrop_topic.portrait_image_url(); ambient_topic.url = backdrop_topic.portrait_image_url();
else
ambient_topic.url = backdrop_topic.url();
if (backdrop_topic.has_related_topic()) {
if (backdrop_topic.related_topic().has_portrait_image_url()) {
ambient_topic.related_image_url =
backdrop_topic.related_topic().portrait_image_url();
} else {
ambient_topic.related_image_url =
backdrop_topic.related_topic().url();
}
}
BuildBackdropTopicDetails(backdrop_topic, ambient_topic); BuildBackdropTopicDetails(backdrop_topic, ambient_topic);
screen_update.next_topics.emplace_back(ambient_topic); screen_update.next_topics.emplace_back(ambient_topic);
} }
......
...@@ -34,6 +34,7 @@ struct ASH_EXPORT PhotoWithDetails { ...@@ -34,6 +34,7 @@ struct ASH_EXPORT PhotoWithDetails {
bool IsNull() const; bool IsNull() const;
gfx::ImageSkia photo; gfx::ImageSkia photo;
gfx::ImageSkia related_photo;
std::string details; std::string details;
}; };
......
...@@ -6,17 +6,22 @@ ...@@ -6,17 +6,22 @@
#include <memory> #include <memory>
#include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ui/glanceable_info_view.h" #include "ash/ambient/ui/glanceable_info_view.h"
#include "ash/ambient/util/ambient_util.h" #include "ash/ambient/util/ambient_util.h"
#include "ash/assistant/ui/assistant_view_ids.h" #include "ash/assistant/ui/assistant_view_ids.h"
#include "base/rand_util.h" #include "base/rand_util.h"
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/metadata/metadata_impl_macros.h" #include "ui/views/metadata/metadata_impl_macros.h"
#include "ui/views/view_class_properties.h"
namespace ash { namespace ash {
...@@ -39,6 +44,37 @@ int translate_y_direction = -1; ...@@ -39,6 +44,37 @@ int translate_y_direction = -1;
int current_x_translation = 0; int current_x_translation = 0;
int current_y_translation = 0; int current_y_translation = 0;
const views::FlexSpecification kUnboundedScaleToZero(
views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded);
gfx::ImageSkia ResizeImage(const gfx::ImageSkia& image,
const gfx::Size& view_size) {
if (image.isNull())
return gfx::ImageSkia();
const double image_width = image.width();
const double image_height = image.height();
const double view_width = view_size.width();
const double view_height = view_size.height();
const double horizontal_ratio = view_width / image_width;
const double vertical_ratio = view_height / image_height;
const double image_ratio = image_height / image_width;
const double view_ratio = view_height / view_width;
// If the image and the container view has the same orientation, e.g. both
// portrait, the |scale| will make the image filled the whole view with
// possible cropping on one direction. If they are in different orientation,
// the |scale| will display the image in the view without any cropping, but
// with empty background.
const double scale = (image_ratio - 1) * (view_ratio - 1) > 0
? std::max(horizontal_ratio, vertical_ratio)
: std::min(horizontal_ratio, vertical_ratio);
const gfx::Size& resized = gfx::ScaleToCeiledSize(image.size(), scale);
return gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::RESIZE_BEST, resized);
}
} // namespace } // namespace
AmbientBackgroundImageView::AmbientBackgroundImageView( AmbientBackgroundImageView::AmbientBackgroundImageView(
...@@ -65,10 +101,43 @@ void AmbientBackgroundImageView::OnGestureEvent(ui::GestureEvent* event) { ...@@ -65,10 +101,43 @@ void AmbientBackgroundImageView::OnGestureEvent(ui::GestureEvent* event) {
} }
} }
void AmbientBackgroundImageView::UpdateImage(const gfx::ImageSkia& img) { void AmbientBackgroundImageView::OnBoundsChanged(
image_view_->SetImage(img); const gfx::Rect& previous_bounds) {
if (!GetVisible())
return;
if (width() == 0)
return;
// When bounds changes, recalculate the visibility of related image view.
UpdateRelatedImageViewVisibility();
}
void AmbientBackgroundImageView::OnViewBoundsChanged(
views::View* observed_view) {
if (observed_view == image_view_)
SetResizedImage(image_view_, image_unscaled_);
else
SetResizedImage(related_image_view_, related_image_unscaled_);
}
void AmbientBackgroundImageView::UpdateImage(
const gfx::ImageSkia& image,
const gfx::ImageSkia& related_image) {
image_unscaled_ = image;
related_image_unscaled_ = related_image;
UpdateGlanceableInfoPosition(); UpdateGlanceableInfoPosition();
const bool has_change = UpdateRelatedImageViewVisibility();
// If there is no change in the visibility of related image view, call
// SetResizedImages() directly. Otherwise it will be called from
// OnViewBoundsChanged().
if (!has_change) {
SetResizedImage(image_view_, image_unscaled_);
SetResizedImage(related_image_view_, related_image_unscaled_);
}
} }
void AmbientBackgroundImageView::UpdateImageDetails( void AmbientBackgroundImageView::UpdateImageDetails(
...@@ -80,15 +149,49 @@ const gfx::ImageSkia& AmbientBackgroundImageView::GetCurrentImage() { ...@@ -80,15 +149,49 @@ const gfx::ImageSkia& AmbientBackgroundImageView::GetCurrentImage() {
return image_view_->GetImage(); return image_view_->GetImage();
} }
gfx::Rect AmbientBackgroundImageView::GetCurrentImageBoundsForTesting() const { gfx::Rect AmbientBackgroundImageView::GetImageBoundsForTesting() const {
return image_view_->GetImageBounds(); return image_view_->GetImageBounds();
} }
gfx::Rect AmbientBackgroundImageView::GetRelatedImageBoundsForTesting() const {
return related_image_view_->GetVisible()
? related_image_view_->GetImageBounds()
: gfx::Rect();
}
void AmbientBackgroundImageView::ResetRelatedImageForTesting() {
related_image_unscaled_ = gfx::ImageSkia();
UpdateRelatedImageViewVisibility();
}
void AmbientBackgroundImageView::InitLayout() { void AmbientBackgroundImageView::InitLayout() {
SetLayoutManager(std::make_unique<views::FillLayout>()); SetLayoutManager(std::make_unique<views::FillLayout>());
// Inits the image view. This view should have the same size of the screen. // Inits container for images.
image_view_ = AddChildView(std::make_unique<views::ImageView>()); image_container_ = AddChildView(std::make_unique<views::View>());
views::FlexLayout* image_layout =
image_container_->SetLayoutManager(std::make_unique<views::FlexLayout>());
image_layout->SetOrientation(views::LayoutOrientation::kHorizontal);
image_layout->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
image_layout->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
image_view_ =
image_container_->AddChildView(std::make_unique<views::ImageView>());
// Set a place holder size for Flex layout to assign bounds.
image_view_->SetPreferredSize(gfx::Size(1, 1));
image_view_->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);
observed_views_.Add(image_view_);
related_image_view_ =
image_container_->AddChildView(std::make_unique<views::ImageView>());
// Set a place holder size for Flex layout to assign bounds.
related_image_view_->SetPreferredSize(gfx::Size(1, 1));
related_image_view_->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToZero);
observed_views_.Add(related_image_view_);
// Set spacing between two images.
related_image_view_->SetProperty(
views::kMarginsKey, gfx::Insets(0, kMarginLeftOfRelatedImageDip, 0, 0));
gfx::Insets shadow_insets = gfx::Insets shadow_insets =
gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues()); gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues());
...@@ -161,6 +264,41 @@ void AmbientBackgroundImageView::UpdateGlanceableInfoPosition() { ...@@ -161,6 +264,41 @@ void AmbientBackgroundImageView::UpdateGlanceableInfoPosition() {
details_label_->layer()->SetTransform(transform); details_label_->layer()->SetTransform(transform);
} }
bool AmbientBackgroundImageView::UpdateRelatedImageViewVisibility() {
const bool did_show_pair = related_image_view_->GetVisible();
const bool show_pair = IsLandscapeOrientation() && HasPairedPortraitImages();
related_image_view_->SetVisible(show_pair);
return did_show_pair != show_pair;
}
void AmbientBackgroundImageView::SetResizedImage(
views::ImageView* image_view,
const gfx::ImageSkia& image_unscaled) {
if (!image_view->GetVisible())
return;
if (image_unscaled.isNull())
return;
image_view->SetImage(ResizeImage(image_unscaled, image_view->size()));
// Intend to update the image origin in image view.
// There is no bounds change or preferred size change when updating image from
// landscape to portrait when device is in portrait orientation because we
// only show one photo. Call ResetImageSize() to trigger UpdateImageOrigin().
image_view->ResetImageSize();
}
bool AmbientBackgroundImageView::IsLandscapeOrientation() const {
return width() > height();
}
bool AmbientBackgroundImageView::HasPairedPortraitImages() const {
const auto& primary_image = image_unscaled_;
return !primary_image.isNull() && !related_image_unscaled_.isNull() &&
primary_image.height() > primary_image.width();
}
BEGIN_METADATA(AmbientBackgroundImageView, views::View) BEGIN_METADATA(AmbientBackgroundImageView, views::View)
END_METADATA END_METADATA
......
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
#include "ash/ambient/ui/ambient_view_delegate.h" #include "ash/ambient/ui/ambient_view_delegate.h"
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "base/scoped_observer.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/metadata/metadata_header_macros.h" #include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/view_observer.h"
namespace views { namespace views {
class Label; class Label;
...@@ -25,7 +27,8 @@ class GlanceableInfoView; ...@@ -25,7 +27,8 @@ class GlanceableInfoView;
// A custom ImageView to display photo image and details information on ambient. // A custom ImageView to display photo image and details information on ambient.
// It also handles specific mouse/gesture events to dismiss ambient when user // It also handles specific mouse/gesture events to dismiss ambient when user
// interacts with the background photos. // interacts with the background photos.
class ASH_EXPORT AmbientBackgroundImageView : public views::View { class ASH_EXPORT AmbientBackgroundImageView : public views::View,
public views::ViewObserver {
public: public:
METADATA_HEADER(AmbientBackgroundImageView); METADATA_HEADER(AmbientBackgroundImageView);
...@@ -34,36 +37,60 @@ class ASH_EXPORT AmbientBackgroundImageView : public views::View { ...@@ -34,36 +37,60 @@ class ASH_EXPORT AmbientBackgroundImageView : public views::View {
AmbientBackgroundImageView& operator=(AmbientBackgroundImageView&) = delete; AmbientBackgroundImageView& operator=(AmbientBackgroundImageView&) = delete;
~AmbientBackgroundImageView() override; ~AmbientBackgroundImageView() override;
// views::View // views::View:
bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMousePressed(const ui::MouseEvent& event) override;
void OnGestureEvent(ui::GestureEvent* event) override; void OnGestureEvent(ui::GestureEvent* event) override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
// Updates the display image. // views::ViewObserver:
void UpdateImage(const gfx::ImageSkia& img); void OnViewBoundsChanged(views::View* observed_view) override;
// Updates the display images.
void UpdateImage(const gfx::ImageSkia& image,
const gfx::ImageSkia& related_image);
// Updates the details for the currently displayed image. // Updates the details for the currently displayed image.
void UpdateImageDetails(const base::string16& details); void UpdateImageDetails(const base::string16& details);
const gfx::ImageSkia& GetCurrentImage(); const gfx::ImageSkia& GetCurrentImage();
gfx::Rect GetCurrentImageBoundsForTesting() const; gfx::Rect GetImageBoundsForTesting() const;
gfx::Rect GetRelatedImageBoundsForTesting() const;
void ResetRelatedImageForTesting();
private: private:
void InitLayout(); void InitLayout();
void UpdateGlanceableInfoPosition(); void UpdateGlanceableInfoPosition();
bool UpdateRelatedImageViewVisibility();
void SetResizedImage(views::ImageView* image_view,
const gfx::ImageSkia& image_unscaled);
// Whether the device is in landscape orientation.
bool IsLandscapeOrientation() const;
bool HasPairedPortraitImages() const;
// Owned by |AmbientController| and should always outlive |this|. // Owned by |AmbientController| and should always outlive |this|.
AmbientViewDelegate* delegate_ = nullptr; AmbientViewDelegate* delegate_ = nullptr;
// View to display the current image on ambient. Owned by the view hierarchy. // View to display current image(s) on ambient. Owned by the view hierarchy.
views::View* image_container_ = nullptr;
views::ImageView* image_view_ = nullptr; views::ImageView* image_view_ = nullptr;
views::ImageView* related_image_view_ = nullptr;
// The unscaled images used for scaling and displaying in different bounds.
gfx::ImageSkia image_unscaled_;
gfx::ImageSkia related_image_unscaled_;
GlanceableInfoView* glanceable_info_view_ = nullptr; GlanceableInfoView* glanceable_info_view_ = nullptr;
// Label to show details text, i.e. attribution, to be displayed for the // Label to show details text, i.e. attribution, to be displayed for the
// current image. Owned by the view hierarchy. // current image. Owned by the view hierarchy.
views::Label* details_label_ = nullptr; views::Label* details_label_ = nullptr;
ScopedObserver<views::View, views::ViewObserver> observed_views_{this};
}; };
} // namespace ash } // namespace ash
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include "ui/compositor/animation_throughput_reporter.h" #include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/layer.h" #include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
...@@ -36,32 +35,6 @@ void ReportSmoothness(int value) { ...@@ -36,32 +35,6 @@ void ReportSmoothness(int value) {
base::UmaHistogramPercentage(kPhotoTransitionSmoothness, value); base::UmaHistogramPercentage(kPhotoTransitionSmoothness, value);
} }
gfx::ImageSkia ResizeImage(const gfx::ImageSkia& image,
const gfx::Size& view_size) {
if (image.isNull())
return gfx::ImageSkia();
const double image_width = image.width();
const double image_height = image.height();
const double view_width = view_size.width();
const double view_height = view_size.height();
const double horizontal_ratio = view_width / image_width;
const double vertical_ratio = view_height / image_height;
const double image_ratio = image_height / image_width;
const double view_ratio = view_height / view_width;
// If the image and the container view has the same orientation, e.g. both
// portrait, the |scale| will make the image filled the whole view with
// possible cropping on one direction. If they are in different orientation,
// the |scale| will display the image in the view without any cropping, but
// with empty background.
const double scale = (image_ratio - 1) * (view_ratio - 1) > 0
? std::max(horizontal_ratio, vertical_ratio)
: std::min(horizontal_ratio, vertical_ratio);
const gfx::Size& resized = gfx::ScaleToCeiledSize(image.size(), scale);
return gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::RESIZE_BEST, resized);
}
} // namespace } // namespace
...@@ -80,14 +53,6 @@ const char* PhotoView::GetClassName() const { ...@@ -80,14 +53,6 @@ const char* PhotoView::GetClassName() const {
return "PhotoView"; return "PhotoView";
} }
void PhotoView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
for (const int index : {0, 1}) {
auto image = images_unscaled_[index];
auto image_resized = ResizeImage(image, size());
image_views_[index]->UpdateImage(image_resized);
}
}
void PhotoView::OnImagesChanged() { void PhotoView::OnImagesChanged() {
// If NeedToAnimate() is true, will start transition animation and // If NeedToAnimate() is true, will start transition animation and
// UpdateImages() when animation completes. Otherwise, update images // UpdateImages() when animation completes. Otherwise, update images
...@@ -123,12 +88,11 @@ void PhotoView::Init() { ...@@ -123,12 +88,11 @@ void PhotoView::Init() {
void PhotoView::UpdateImages() { void PhotoView::UpdateImages() {
auto* model = delegate_->GetAmbientBackendModel(); auto* model = delegate_->GetAmbientBackendModel();
auto& next_image = model->GetNextImage(); auto& next_image = model->GetNextImage();
images_unscaled_[image_index_] = next_image.photo; if (next_image.photo.isNull())
if (images_unscaled_[image_index_].isNull())
return; return;
auto next_resized = ResizeImage(images_unscaled_[image_index_], size()); image_views_[image_index_]->UpdateImage(next_image.photo,
image_views_[image_index_]->UpdateImage(next_resized); next_image.related_photo);
image_views_[image_index_]->UpdateImageDetails( image_views_[image_index_]->UpdateImageDetails(
base::UTF8ToUTF16(next_image.details)); base::UTF8ToUTF16(next_image.details));
image_index_ = 1 - image_index_; image_index_ = 1 - image_index_;
...@@ -178,7 +142,7 @@ void PhotoView::OnImplicitAnimationsCompleted() { ...@@ -178,7 +142,7 @@ void PhotoView::OnImplicitAnimationsCompleted() {
bool PhotoView::NeedToAnimateTransition() const { bool PhotoView::NeedToAnimateTransition() const {
// Can do transition animation if both two images in |images_unscaled_| are // Can do transition animation if both two images in |images_unscaled_| are
// not nullptr. Check the image index 1 is enough. // not nullptr. Check the image index 1 is enough.
return !images_unscaled_[1].isNull(); return !image_views_[1]->GetCurrentImage().isNull();
} }
const gfx::ImageSkia& PhotoView::GetCurrentImagesForTesting() { const gfx::ImageSkia& PhotoView::GetCurrentImagesForTesting() {
......
...@@ -35,7 +35,6 @@ class ASH_EXPORT PhotoView : public views::View, ...@@ -35,7 +35,6 @@ class ASH_EXPORT PhotoView : public views::View,
// views::View: // views::View:
const char* GetClassName() const override; const char* GetClassName() const override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
// AmbientBackendModelObserver: // AmbientBackendModelObserver:
void OnImagesChanged() override; void OnImagesChanged() override;
...@@ -47,7 +46,9 @@ class ASH_EXPORT PhotoView : public views::View, ...@@ -47,7 +46,9 @@ class ASH_EXPORT PhotoView : public views::View,
friend class AmbientAshTestBase; friend class AmbientAshTestBase;
void Init(); void Init();
void UpdateImages(); void UpdateImages();
void StartTransitionAnimation(); void StartTransitionAnimation();
// Return if can start transition animation. // Return if can start transition animation.
...@@ -62,9 +63,6 @@ class ASH_EXPORT PhotoView : public views::View, ...@@ -62,9 +63,6 @@ class ASH_EXPORT PhotoView : public views::View,
// Image containers used for animation. Owned by view hierarchy. // Image containers used for animation. Owned by view hierarchy.
AmbientBackgroundImageView* image_views_[2]{nullptr, nullptr}; AmbientBackgroundImageView* image_views_[2]{nullptr, nullptr};
// The unscaled images used for scaling and displaying in different bounds.
gfx::ImageSkia images_unscaled_[2];
// The index of |image_views_| to update the next image. // The index of |image_views_| to update the next image.
int image_index_ = 0; int image_index_ = 0;
}; };
......
...@@ -32,8 +32,9 @@ TEST_F(AmbientPhotoViewTest, ShouldResizePortraitImageForPortraitScreen) { ...@@ -32,8 +32,9 @@ TEST_F(AmbientPhotoViewTest, ShouldResizePortraitImageForPortraitScreen) {
// Image should be full width. Image height should extend above and below the // Image should be full width. Image height should extend above and below the
// visible part of the screen. // visible part of the screen.
ASSERT_EQ(image_view->GetCurrentImageBoundsForTesting(), ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-200, /*width=*/600, /*height=*/1200)); gfx::Rect(/*x=*/0, /*y=*/-200, /*width=*/600, /*height=*/1200));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
} }
// Test that image is scaled to fill screen width when the image is landscape // Test that image is scaled to fill screen width when the image is landscape
...@@ -52,14 +53,92 @@ TEST_F(AmbientPhotoViewTest, ShouldResizeLandscapeImageForPortraitScreen) { ...@@ -52,14 +53,92 @@ TEST_F(AmbientPhotoViewTest, ShouldResizeLandscapeImageForPortraitScreen) {
// Image should be full width. Image should have equal empty space top and // Image should be full width. Image should have equal empty space top and
// bottom. // bottom.
ASSERT_EQ(image_view->GetCurrentImageBoundsForTesting(), ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/200, /*width=*/600, /*height=*/400)); gfx::Rect(/*x=*/0, /*y=*/200, /*width=*/600, /*height=*/400));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
} }
// Test that image is scaled to fill screen height when the image is portrait // Test that two portrait images are scaled and tiled to fill screen when the
// and screen is landscape. There will be black bars to the left and right of // screen is landscape.
// the image, as the width of the image is less than the width of the screen. TEST_F(AmbientPhotoViewTest, ShouldTileTwoPortraitImagesForLandscapeScreen) {
TEST_F(AmbientPhotoViewTest, ShouldResizePortraitImageForLandscapeScreen) { SetPhotoViewImageSize(/*width=*/10, /*height=*/20);
UpdateDisplay("808x600");
ShowAmbientScreen();
FastForwardToNextImage();
auto* image_view = GetAmbientBackgroundImageView();
// Will tile two portrait images. Each image will fill 400x600 area with 8px
// spaing in between.
// Image should be full width. Image height should extend above and below the
// visible part of the view.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
}
// Test that two portrait images are scaled and tiled to fill screen when the
// screen is landscape. For odd screen width, the first image will take one more
// pixel.
TEST_F(AmbientPhotoViewTest,
ShouldTileTwoPortraitImagesForLandscapeScreenWithOddWidth) {
SetPhotoViewImageSize(/*width=*/10, /*height=*/20);
constexpr int kScreenWidth = 809;
constexpr int kScreenHeight = 600;
std::string display_size = base::NumberToString(kScreenWidth) + "x" +
base::NumberToString(kScreenHeight);
UpdateDisplay(display_size);
ShowAmbientScreen();
FastForwardToNextImage();
auto* image_view = GetAmbientBackgroundImageView();
// Will tile two portrait images.
// The first image will fill 401x802 area with 8px spaing in between. Image
// should be full width. Image height should extend above and below the
// visible part of the view.
const int image_width = (kScreenWidth - kMarginLeftOfRelatedImageDip + 1) / 2;
const int image_height = 20 * image_width / 10;
const int y = (kScreenHeight - image_height) / 2;
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/y, /*width=*/image_width,
/*height=*/image_height));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
}
// Test that landscape images will not be tiled when screen is landscape.
TEST_F(AmbientPhotoViewTest,
ShouldNotTileTwoLandscapeImagesForLandscapeScreen) {
SetPhotoViewImageSize(/*width=*/20, /*height=*/10);
UpdateDisplay("808x600");
ShowAmbientScreen();
FastForwardToNextImage();
auto* image_view = GetAmbientBackgroundImageView();
// Only show one landscape image.
// Image should be full height. Image width should extend equally to the left
// and right of the visible part of the screen.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/-196, /*y=*/0, /*width=*/1200, /*height=*/600));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
}
// Test that only have one available image will not be tiled when screen is
// landscape.
TEST_F(AmbientPhotoViewTest,
ShouldNotTileIfRelatedImageIsNullForLandscapeScreen) {
SetPhotoViewImageSize(/*width=*/10, /*height=*/20); SetPhotoViewImageSize(/*width=*/10, /*height=*/20);
UpdateDisplay("800x600"); UpdateDisplay("800x600");
...@@ -70,17 +149,25 @@ TEST_F(AmbientPhotoViewTest, ShouldResizePortraitImageForLandscapeScreen) { ...@@ -70,17 +149,25 @@ TEST_F(AmbientPhotoViewTest, ShouldResizePortraitImageForLandscapeScreen) {
auto* image_view = GetAmbientBackgroundImageView(); auto* image_view = GetAmbientBackgroundImageView();
// Image should be full height. Image width should have equal empty space on // Remove the related image.
// left and right. image_view->ResetRelatedImageForTesting();
ASSERT_EQ(image_view->GetCurrentImageBoundsForTesting(),
gfx::Rect(/*x=*/250, /*y=*/0, /*width=*/300, /*height=*/600)); // Trigger layout.
UpdateDisplay("808x600");
// Only show one portrait image.
// Image should be full height. Image should have equal empty space left and
// right.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/254, /*y=*/0, /*width=*/300, /*height=*/600));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
} }
// Test that image is scaled to fill screen height when the image is landscape // Test that image is scaled to fill screen height when the image is landscape
// and the screen is landscape. The image will be zoomed in and the left and // and the screen is landscape. The image will be zoomed in and the left and
// right will be cut off, as the width of the image is greater than the width of // right will be cut off, as the width of the image is greater than the width of
// the screen. // the screen.
TEST_F(AmbientPhotoViewTest, ShouldResizeLandscapeImageForFillLandscapeScreen) { TEST_F(AmbientPhotoViewTest, ShouldResizeLandscapeImageForLandscapeScreen) {
SetPhotoViewImageSize(/*width=*/30, /*height=*/20); SetPhotoViewImageSize(/*width=*/30, /*height=*/20);
UpdateDisplay("800x600"); UpdateDisplay("800x600");
...@@ -93,8 +180,131 @@ TEST_F(AmbientPhotoViewTest, ShouldResizeLandscapeImageForFillLandscapeScreen) { ...@@ -93,8 +180,131 @@ TEST_F(AmbientPhotoViewTest, ShouldResizeLandscapeImageForFillLandscapeScreen) {
// Image should be full height. Image width should extend equally to the left // Image should be full height. Image width should extend equally to the left
// and right of the visible part of the screen. // and right of the visible part of the screen.
ASSERT_EQ(image_view->GetCurrentImageBoundsForTesting(), ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/-50, /*y=*/0, /*width=*/900, /*height=*/600)); gfx::Rect(/*x=*/-50, /*y=*/0, /*width=*/900, /*height=*/600));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
}
// Test that when rotates to portrait screen, will dynamically only show one
// portrait image.
TEST_F(AmbientPhotoViewTest, ShouldNotTileWhenRotateToPortraitScreen) {
SetPhotoViewImageSize(/*width=*/10, /*height=*/20);
UpdateDisplay("808x600");
ShowAmbientScreen();
FastForwardToNextImage();
auto* image_view = GetAmbientBackgroundImageView();
// Will tile two portrait images. Each image will fill 400x600 area with 8px
// spaing in between.
// Image should be full width. Image height should extend above and below the
// visible part of the view.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
// Rotate screen.
UpdateDisplay("600x808");
// Only one image will show.
// Image should be full width. Image height should extend above and below the
// visible part of the screen.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-196, /*width=*/600, /*height=*/1200));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
}
// Test that when rotates to landscape screen, will dynamically tile two
// portrait images.
TEST_F(AmbientPhotoViewTest, ShouldTileWhenRotateToLandscapeScreen) {
SetPhotoViewImageSize(/*width=*/10, /*height=*/20);
UpdateDisplay("600x808");
ShowAmbientScreen();
FastForwardToNextImage();
auto* image_view = GetAmbientBackgroundImageView();
// Only one image will show.
// Image should be full width. Image height should extend above and below the
// visible part of the screen.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-196, /*width=*/600, /*height=*/1200));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(), gfx::Rect());
// Rotate screen.
UpdateDisplay("808x600");
// Will tile two portrait images. Each image will fill 400x600 area with 8px
// spaing in between.
// Image should be full width. Image height should extend above and below the
// visible part of the view.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
}
// Test that two protrat images will resize when bounds changes in landscape.
TEST_F(AmbientPhotoViewTest, ShouldResizeTiledPortraitImagesWhenBoundsChanged) {
SetPhotoViewImageSize(/*width=*/10, /*height=*/20);
UpdateDisplay("808x600");
ShowAmbientScreen();
FastForwardToNextImage();
auto* image_view = GetAmbientBackgroundImageView();
// Will tile two portrait images. Each image will fill 400x600 area with 8px
// spaing in between.
// Image should be full width. Image height should extend above and below the
// visible part of the view.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-100, /*width=*/400, /*height=*/800));
// Bounds changes so that image will be shown in portrait view.
UpdateDisplay("508x200");
// Will tile two portrait images. Each image will fill 250x200 area with 8px
// spaing in between.
// Image should be full height. Image should have equal empty space left and
// right.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/75, /*y=*/0, /*width=*/100, /*height=*/200));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/75, /*y=*/0, /*width=*/100, /*height=*/200));
// Bounds changes so that image will be shown in portrait view.
UpdateDisplay("308x200");
// Will tile two portrait images. Each image will fill 150x200 area with 8px
// spaing in between.
// Image should be full width. Image height should extend above and below the
// visible part of the view.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-50, /*width=*/150, /*height=*/300));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/-50, /*width=*/150, /*height=*/300));
// Bounds changes to exact aspect ratio of the image.
UpdateDisplay("208x200");
// Will tile two portrait images. Each image will fill 100x200 area with 8px
// spaing in between.
// Image should be full width and height.
ASSERT_EQ(image_view->GetImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/0, /*width=*/100, /*height=*/200));
ASSERT_EQ(image_view->GetRelatedImageBoundsForTesting(),
gfx::Rect(/*x=*/0, /*y=*/0, /*width=*/100, /*height=*/200));
} }
} // namespace ash } // namespace ash
...@@ -31,10 +31,6 @@ AmbientModeTopic& AmbientModeTopic::operator=(const AmbientModeTopic&) = ...@@ -31,10 +31,6 @@ 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,9 +27,6 @@ struct ASH_PUBLIC_EXPORT AmbientModeTopic { ...@@ -27,9 +27,6 @@ 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 // Details, i.e. the attribution, to be displayed for the current photo on
// ambient. // ambient.
std::string details; std::string details;
...@@ -37,9 +34,8 @@ struct ASH_PUBLIC_EXPORT AmbientModeTopic { ...@@ -37,9 +34,8 @@ struct ASH_PUBLIC_EXPORT AmbientModeTopic {
// Image url. // Image url.
std::string url; std::string url;
// Optional for non-cropped portrait style images. The same image as in // Only support portrait image tiling in landscape orientation.
// |url| but it is not cropped and is better for portrait displaying. base::Optional<std::string> related_image_url;
base::Optional<std::string> portrait_image_url;
}; };
// WeatherInfo contains the weather information we need for rendering a // WeatherInfo contains the weather information we need for rendering a
......
...@@ -81,6 +81,7 @@ void FakeAmbientBackendControllerImpl::FetchScreenUpdateInfo( ...@@ -81,6 +81,7 @@ void FakeAmbientBackendControllerImpl::FetchScreenUpdateInfo(
ash::AmbientModeTopic topic; ash::AmbientModeTopic topic;
topic.url = kFakeUrl; topic.url = kFakeUrl;
topic.details = kFakeDetails; topic.details = kFakeDetails;
topic.related_image_url = kFakeUrl;
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