// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_
#define ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_
#include <string>
#include <vector>
#include "ash/ambient/ambient_constants.h"
#include "ash/ambient/model/ambient_photo_config.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "base/containers/circular_deque.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/gfx/image/image_skia.h"
namespace ash {
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;
gfx::ImageSkia related_photo;
std::string details;
std::string related_details;
// Hash of this image data. Used for de-duping images.
std::string hash;
// Whether the image is portrait or not.
bool is_portrait = false;
::ambient::TopicType topic_type = ::ambient::TopicType::kOther;
};
// Stores necessary information fetched from the backdrop server to render
// the photo frame in Ambient Mode. Owned by |AmbientController|.
class ASH_EXPORT AmbientBackendModel {
public:
explicit AmbientBackendModel(AmbientPhotoConfig photo_config);
AmbientBackendModel(const AmbientBackendModel&) = delete;
AmbientBackendModel& operator=(AmbientBackendModel&) = delete;
~AmbientBackendModel();
void AddObserver(AmbientBackendModelObserver* observer);
void RemoveObserver(AmbientBackendModelObserver* observer);
// If enough images are loaded to start ambient mode.
bool ImagesReady() const;
// Add image to local storage.
void AddNextImage(const PhotoWithDetails& photo);
// Returns true if |hash| would cause an identical image to appear twice in a
// row. For example:
// {A, B} + B => true
// {A, B} + A => false
// {A, _} + B => false
// {A, _} + A => true
bool IsHashDuplicate(const std::string& hash) const;
// Record that fetching an image has failed.
void AddImageFailure();
void ResetImageFailures();
bool ImageLoadingFailed();
// Clear local storage.
void Clear();
// Sets the new AmbientPhotoConfig to use. This automatically |Clear()|s the
// model of any existing topics.
void SetPhotoConfig(AmbientPhotoConfig photo_config);
// Returns all available decoded topics. The number of decoded topics in the
// output will always be <= |AmbientPhotoConfig.num_decoded_topics_to_buffer|.
//
// Every PhotoWithDetails instance in the output shall be non-null.
const base::circular_deque<PhotoWithDetails>& all_decoded_topics() const {
return all_decoded_topics_;
}
// Gets the 2 oldest decoded topics. It's possible to accomplish this as well
// by calling GetAllAvailableDecodedTopics() directly, but this wrapper
// function is provided as a convenience.
//
// If an output PhotoWithDetails argument is nullptr, that specific topic is
// ignored and not fetched.
//
// If one of the requested topics is unavailable, its corresponding output
// argument is set to an empty PhotoWithDetails instance.
void GetCurrentAndNextImages(PhotoWithDetails* current_image_out,
PhotoWithDetails* next_image_out) const;
base::TimeDelta GetPhotoRefreshInterval() const;
const AmbientPhotoConfig& photo_config() const { return photo_config_; }
private:
friend class AmbientBackendModelTest;
friend class AmbientAshTestBase;
void NotifyImageAdded();
void NotifyImagesReady();
void OnImagesReadyTimeoutFired();
AmbientPhotoConfig photo_config_;
std::vector<AmbientModeTopic> topics_;
// All available decoded topics. The size of the ring buffer is capped
// according to |AmbientPhotoConfig.num_decoded_topics_to_buffer|. The most
// recently decoded topics are pushed to the back of the ring buffer and the
// oldest topics are popped from the front.
base::circular_deque<PhotoWithDetails> all_decoded_topics_;
base::OneShotTimer images_ready_timeout_timer_;
bool images_ready_timed_out_ = false;
// The number of consecutive failures to load the next image.
int failures_ = 0;
base::ObserverList<AmbientBackendModelObserver> observers_;
int buffer_length_for_testing_ = -1;
};
} // namespace ash
#endif // ASH_AMBIENT_MODEL_AMBIENT_BACKEND_MODEL_H_