| // Copyright 2019 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. |
| |
| #ifndef ASH_AMBIENT_AMBIENT_PHOTO_CONTROLLER_H_ |
| #define ASH_AMBIENT_AMBIENT_PHOTO_CONTROLLER_H_ |
| |
| #include <memory> |
| #include <ostream> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/ambient/ambient_constants.h" |
| #include "ash/ambient/ambient_photo_cache.h" |
| #include "ash/ambient/model/ambient_backend_model.h" |
| #include "ash/ambient/model/ambient_photo_config.h" |
| #include "ash/ambient/model/ambient_topic_queue.h" |
| #include "ash/ambient/ui/ambient_view_delegate.h" |
| #include "ash/ash_export.h" |
| #include "ash/public/cpp/ambient/ambient_backend_controller.h" |
| #include "ash/public/cpp/ambient/proto/photo_cache_entry.pb.h" |
| #include "base/callback_forward.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/scoped_observation.h" |
| #include "base/timer/timer.h" |
| #include "net/base/backoff_entry.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace gfx { |
| class ImageSkia; |
| } // namespace gfx |
| |
| namespace ash { |
| |
| class AmbientClient; |
| class AmbientAccessTokenController; |
| |
| // Class to handle photos in ambient mode. |
| // |
| // Terminology: |
| // |
| // Topic - A primary and optional related photo specified by the IMAX server. |
| // |
| // Fetch Topics - Request new topics from the IMAX server. After they're |
| // fetched, the controller just has urls for the primary/optional |
| // photos in each returned topic. |
| // |
| // Download Topic - Download the encoded primary/related photos from their |
| // corresponding urls. |
| // |
| // Save Topic - Write the topic's encoded photos to disk for future re-use. |
| // Helpful in future cases where ambient mode starts and there's no |
| // internet. |
| // |
| // Load Topic - Read a previously saved topic's encoded photos from disk. |
| // |
| // Decode Topic - Decode the topic's photos and commit them to the |
| // AmbientBackendModel. |
| // |
| // Prepare Topic - A term that aggregates all of the steps above: |
| // 1) Either a) fetch/download/save new topic or b) load existing topic |
| // 2) Decode topic and commit to the model. |
| // |
| // Topic Set - A group of topics for the UI to display in one cycle, capped by |
| // |AmbientPhotoConfig.topic_set_size|. |
| // |
| // The controller's state machine: |
| // |
| // kInactive |
| // | |
| // | |
| // v |
| // kPreparingNextTopicSet <----- |
| // | | |
| // | | |
| // v | |
| // kWaitingForNextMarker ------- |
| // |
| // kInactive: |
| // The controller is idle, and the model has no decoded topics in it. This is |
| // the initial state when the controller is constructed. Although not |
| // illustrated above, the controller can transition to this state from any of |
| // the other states via a call to StopScreenUpdate(). |
| // |
| // |
| // kPreparingNextTopicSet (a.k.a. "refreshing" the model's topics): |
| // The very first time this state is reached, the UI has not started rendering |
| // yet, and the controller is preparing initial sets of topics. The |
| // AmbientPhotoConfig dictates how many sets to prepare initially. This state is |
| // initially triggered by a call to StartScreenUpdate(), and it ends when |
| // AmbientBackendModel::ImagesReady() is true. |
| // |
| // kWaitingForNextMarker: |
| // The UI is rendering the decoded topics currently in the model, and the |
| // controller is idle. It's waiting for the right marker(s) to be hit in the UI |
| // before it becomes active and starts preparing the next set of topics. |
| // |
| // kPreparingNextTopicSet (again): |
| // A target marker has been hit, and the controller immediately starts preparing |
| // the next set of topics. Unlike the first time this state was hit, there |
| // is only ever 1 topic set prepared, and the UI is rendering while the topics |
| // are being prepared. After the topic set is completely prepared, the |
| // controller goes back to WAITING_FOR_NEXT_MARKER. If another target marker is |
| // received while the controller is still preparing a topic set, the controller |
| // will simply reset its internal "counter" to 0 and start preparing a brand new |
| // set. |
| class ASH_EXPORT AmbientPhotoController : public AmbientViewEventHandler { |
| public: |
| AmbientPhotoController(AmbientClient& ambient_client, |
| AmbientAccessTokenController& access_token_controller, |
| AmbientPhotoConfig photo_config); |
| |
| AmbientPhotoController(const AmbientPhotoController&) = delete; |
| AmbientPhotoController& operator=(const AmbientPhotoController&) = delete; |
| |
| ~AmbientPhotoController() override; |
| |
| // Start/stop updating the screen contents. |
| // We need different logics to update photos and weather info because they |
| // have different refreshing intervals. |
| void StartScreenUpdate( |
| std::unique_ptr<AmbientTopicQueue::Delegate> topic_queue_delegate); |
| void StopScreenUpdate(); |
| bool IsScreenUpdateActive() const; |
| |
| AmbientBackendModel* ambient_backend_model() { |
| return &ambient_backend_model_; |
| } |
| |
| base::OneShotTimer& backup_photo_refresh_timer_for_testing() { |
| return backup_photo_refresh_timer_; |
| } |
| |
| // AmbientViewEventHandler: |
| void OnMarkerHit(AmbientPhotoConfig::Marker marker) override; |
| |
| // Clear cache when Settings changes. |
| void ClearCache(); |
| |
| private: |
| enum class State { kInactive, kWaitingForNextMarker, kPreparingNextTopicSet }; |
| |
| friend class AmbientAshTestBase; |
| friend class AmbientPhotoControllerTest; |
| friend std::ostream& operator<<(std::ostream& os, State state); |
| |
| // Initialize variables. |
| void Init(std::unique_ptr<AmbientTopicQueue::Delegate> topic_queue_delegate); |
| |
| void FetchWeather(); |
| |
| void ScheduleFetchBackupImages(); |
| |
| // Download backup cache images. |
| void FetchBackupImages(); |
| |
| void OnBackupImageFetched(bool success); |
| |
| void OnTopicsAvailableInQueue(AmbientTopicQueue::WaitResult wait_result); |
| |
| // Clear temporary image data to prepare next photos. |
| void ResetImageData(); |
| |
| void ReadPhotoFromTopicQueue(); |
| |
| void TryReadPhotoFromCache(); |
| |
| void OnPhotoRawDataDownloaded(bool is_related_image, |
| base::RepeatingClosure on_done, |
| std::string&& data); |
| |
| void OnAllPhotoRawDataDownloaded(); |
| |
| void OnAllPhotoRawDataAvailable(bool from_downloading); |
| |
| void OnPhotoRawDataSaved(bool from_downloading); |
| |
| void DecodePhotoRawData(bool from_downloading, |
| bool is_related_image, |
| base::RepeatingClosure on_done, |
| const std::string& data); |
| |
| void OnPhotoDecoded(bool from_downloading, |
| bool is_related_image, |
| base::RepeatingClosure on_done, |
| const gfx::ImageSkia& image); |
| |
| void OnAllPhotoDecoded(bool from_downloading, |
| const std::string& hash); |
| |
| void StartDownloadingWeatherConditionIcon( |
| const absl::optional<WeatherInfo>& weather_info); |
| |
| // Invoked upon completion of the weather icon download, |icon| can be a null |
| // image if the download attempt from the url failed. |
| void OnWeatherConditionIconDownloaded(float temp_f, |
| bool show_celsius, |
| const gfx::ImageSkia& icon); |
| |
| void set_photo_cache_for_testing( |
| std::unique_ptr<AmbientPhotoCache> photo_cache) { |
| photo_cache_ = std::move(photo_cache); |
| } |
| |
| AmbientPhotoCache* get_photo_cache_for_testing() { |
| return photo_cache_.get(); |
| } |
| |
| void set_backup_photo_cache_for_testing( |
| std::unique_ptr<AmbientPhotoCache> photo_cache) { |
| backup_photo_cache_ = std::move(photo_cache); |
| } |
| |
| AmbientPhotoCache* get_backup_photo_cache_for_testing() { |
| return backup_photo_cache_.get(); |
| } |
| |
| void FetchTopicsForTesting(); |
| |
| void FetchImageForTesting(); |
| |
| void FetchBackupImagesForTesting(); |
| |
| // Kicks off preparation of the next topic. |
| void StartPreparingNextTopic(); |
| |
| std::unique_ptr<AmbientTopicQueue> ambient_topic_queue_; |
| AmbientBackendModel ambient_backend_model_; |
| |
| // The timer to refresh backup cache photos. |
| base::OneShotTimer backup_photo_refresh_timer_; |
| |
| // The timer to refresh weather information. |
| base::RepeatingTimer weather_refresh_timer_; |
| |
| State state_ = State::kInactive; |
| |
| // The index of a topic to download. |
| size_t topic_index_ = 0; |
| |
| // Current index of cached image to read and display when failure happens. |
| // The image file of this index may not exist or may not be valid. It will try |
| // to read from the next cached file by increasing this index by 1. |
| int cache_index_for_display_ = 0; |
| |
| // Current index of backup cached image to display when no other cached images |
| // are available. |
| size_t backup_cache_index_for_display_ = 0; |
| |
| // Current index of cached image to save for the latest downloaded photo. |
| // The write command could fail. This index will increase 1 no matter writing |
| // successfully or not. But theoretically we could not to change this index if |
| // failures happen. |
| int cache_index_for_store_ = 0; |
| |
| // Cached image may not exist or valid. This is the max times of attempts to |
| // read cached images. |
| int retries_to_read_from_cache_ = kMaxNumberOfCachedImages; |
| |
| int backup_retries_to_read_from_cache_ = 0; |
| |
| // Backoff to resume fetch images. |
| net::BackoffEntry resume_fetch_image_backoff_; |
| |
| std::unique_ptr<AmbientPhotoCache> photo_cache_; |
| std::unique_ptr<AmbientPhotoCache> backup_photo_cache_; |
| |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| |
| // Temporary data store when fetching images and details. |
| ::ambient::PhotoCacheEntry cache_entry_; |
| gfx::ImageSkia image_; |
| gfx::ImageSkia related_image_; |
| |
| // Tracks the number of topics that have been prepared since the controller |
| // last transitioned to the |kPreparingNextTopicSet| state. |
| size_t num_topics_prepared_ = 0; |
| |
| base::WeakPtrFactory<AmbientPhotoController> weak_factory_{this}; |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_AMBIENT_AMBIENT_PHOTO_CONTROLLER_H_ |