// Copyright 2022 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 "ash/ambient/ambient_weather_controller.h" #include #include #include "ash/ambient/ambient_controller.h" #include "ash/ambient/model/ambient_weather_model.h" #include "ash/public/cpp/ambient/ambient_backend_controller.h" #include "ash/public/cpp/image_downloader.h" #include "ash/shell.h" #include "base/bind.h" #include "base/callback.h" #include "base/check.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace ash { namespace { // TODO(jamescook): Rename to "ambient weather". constexpr net::NetworkTrafficAnnotationTag kAmbientPhotoControllerTag = net::DefineNetworkTrafficAnnotation("ambient_photo_controller", R"( semantics { sender: "Ambient photo" description: "Download ambient image weather icon from Google." trigger: "Triggered periodically when the battery is charged and the user " "is idle." data: "None." destination: GOOGLE_OWNED_SERVICE } policy { cookies_allowed: NO setting: "This feature is off by default and can be overridden by user." policy_exception_justification: "This feature is set by user settings.ambient_mode.enabled pref. " "The user setting is per device and cannot be overriden by admin." })"); void DownloadImageFromUrl( const std::string& url, base::OnceCallback callback) { DCHECK(!url.empty()); // During shutdown, we may not have `ImageDownloader` when reach here. if (!ImageDownloader::Get()) return; ImageDownloader::Get()->Download(GURL(url), kAmbientPhotoControllerTag, std::move(callback)); } } // namespace AmbientWeatherController::AmbientWeatherController() : weather_model_(std::make_unique()) {} AmbientWeatherController::~AmbientWeatherController() = default; void AmbientWeatherController::FetchWeather() { Shell::Get() ->ambient_controller() ->ambient_backend_controller() ->FetchWeather(base::BindOnce( &AmbientWeatherController::StartDownloadingWeatherConditionIcon, weak_factory_.GetWeakPtr())); } void AmbientWeatherController::StartDownloadingWeatherConditionIcon( const absl::optional& weather_info) { if (!weather_info) { LOG(WARNING) << "No weather info included in the response."; return; } if (!weather_info->temp_f.has_value()) { LOG(WARNING) << "No temperature included in weather info."; return; } if (weather_info->condition_icon_url.value_or(std::string()).empty()) { LOG(WARNING) << "No value found for condition icon url in the weather info " "response."; return; } // Ideally we should avoid downloading from the same url again to reduce the // overhead, as it's unlikely that the weather condition is changing // frequently during the day. // TODO(meilinw): avoid repeated downloading by caching the last N url hashes, // where N should depend on the icon image size. DownloadImageFromUrl( weather_info->condition_icon_url.value(), base::BindOnce( &AmbientWeatherController::OnWeatherConditionIconDownloaded, weak_factory_.GetWeakPtr(), weather_info->temp_f.value(), weather_info->show_celsius)); } void AmbientWeatherController::OnWeatherConditionIconDownloaded( float temp_f, bool show_celsius, const gfx::ImageSkia& icon) { // For now we only show the weather card when both fields have values. // TODO(meilinw): optimize the behavior with more specific error handling. if (icon.isNull()) return; weather_model_->UpdateWeatherInfo(icon, temp_f, show_celsius); } } // namespace ash