Add birch weather provider
Adds a birch item struct for weather, with some updates to the generic
BirchItem - adds an icon image model, and changes the title to u16
string.
Introduces birch weather provider, that uses ambient mode backend to
query current weather state - not using the ambient mode weather model
at this point so the network requests tags when downloading icon are
different, and eventually, the backend call will be updated to use a
different weather client ID. Also, birch currently does not use polling
for weather changes.
Still todo - check whether geolocation is enabled before requesting
weather.
BUG= b/323229328
Change-Id: I5494f0afe697be5d053b201b494a0e745d114fd6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5255822
Reviewed-by: Matthew Mourgos <mmourgos@chromium.org>
Reviewed-by: Eric Sum <esum@google.com>
Commit-Queue: Toni Barzic <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1256514}
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index a063051..9b6c3be 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -333,6 +333,8 @@
"birch/birch_item.h",
"birch/birch_model.cc",
"birch/birch_model.h",
+ "birch/birch_weather_provider.cc",
+ "birch/birch_weather_provider.h",
"bluetooth_devices_observer.cc",
"bluetooth_devices_observer.h",
"booting/booting_animation_controller.cc",
@@ -3314,6 +3316,7 @@
"assistant/util/deep_link_util_unittest.cc",
"assistant/util/resource_util_unittest.cc",
"birch/birch_model_unittest.cc",
+ "birch/birch_weather_provider_unittest.cc",
"bubble/bubble_event_filter_unittest.cc",
"bubble/bubble_utils_unittest.cc",
"capture_mode/capture_audio_mixing_unittests.cc",
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.cc b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
index 3268872..06be7bc 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.cc
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
@@ -219,6 +219,8 @@
WeatherInfo weather_info;
const auto& list_result = result.GetList();
+ weather_info.condition_description = GetStringValue(
+ list_result, backdrop::WeatherInfo::kConditionDescriptionFieldNumber);
weather_info.condition_icon_url = GetStringValue(
list_result, backdrop::WeatherInfo::kConditionIconUrlFieldNumber);
weather_info.temp_f =
diff --git a/ash/birch/birch_item.cc b/ash/birch/birch_item.cc
index d45c5cf..91b668b 100644
--- a/ash/birch/birch_item.cc
+++ b/ash/birch/birch_item.cc
@@ -12,13 +12,15 @@
namespace ash {
-BirchItem::BirchItem(const std::string& title) : title(title) {}
+BirchItem::BirchItem(const std::u16string& title, ui::ImageModel icon)
+ : title(title), icon(std::move(icon)) {}
BirchItem::~BirchItem() = default;
BirchFileItem::BirchFileItem(const base::FilePath& file_path,
const std::optional<base::Time>& timestamp)
- : BirchItem(file_path.BaseName().value()),
+ : BirchItem(base::UTF8ToUTF16(file_path.BaseName().value()),
+ ui::ImageModel()),
file_path(file_path),
timestamp(timestamp) {}
@@ -26,21 +28,38 @@
std::string BirchFileItem::ToString() const {
std::stringstream ss;
- ss << "title: " << title << ", file_path:" << file_path;
+ ss << "File item : {title: " << base::UTF16ToUTF8(title)
+ << ", file_path:" << file_path;
if (timestamp.has_value()) {
ss << ", timestamp: "
<< base::UTF16ToUTF8(
base::TimeFormatShortDateAndTime(timestamp.value()));
}
+ ss << "}";
return ss.str();
}
-BirchTabItem::BirchTabItem(const std::string& title,
+BirchWeatherItem::BirchWeatherItem(const std::u16string& weather_description,
+ const std::u16string& temperature,
+ ui::ImageModel icon)
+ : BirchItem(weather_description, std::move(icon)),
+ temperature(temperature) {}
+
+BirchWeatherItem::~BirchWeatherItem() = default;
+
+std::string BirchWeatherItem::ToString() const {
+ std::stringstream ss;
+ ss << "Weather item: {title: " << base::UTF16ToUTF8(title)
+ << ", temperature:" << base::UTF16ToUTF8(temperature) << "}";
+ return ss.str();
+}
+
+BirchTabItem::BirchTabItem(const std::u16string& title,
const GURL& url,
const base::Time& timestamp,
const GURL& favicon_url,
const std::string& session_name)
- : BirchItem(title),
+ : BirchItem(title, ui::ImageModel()),
url(url),
timestamp(timestamp),
favicon_url(favicon_url),
@@ -52,8 +71,9 @@
std::string BirchTabItem::ToString() const {
std::stringstream ss;
- ss << "title: " << title << ", url:" << url << ", timestamp:" << timestamp
- << ", favicon_url:" << favicon_url << ", session_name:" << session_name;
+ ss << "title: " << base::UTF16ToUTF8(title) << ", url:" << url
+ << ", timestamp:" << timestamp << ", favicon_url:" << favicon_url
+ << ", session_name:" << session_name;
return ss.str();
}
diff --git a/ash/birch/birch_item.h b/ash/birch/birch_item.h
index fb87c24..23d54d0 100644
--- a/ash/birch/birch_item.h
+++ b/ash/birch/birch_item.h
@@ -11,20 +11,22 @@
#include "ash/ash_export.h"
#include "base/files/file_path.h"
#include "base/time/time.h"
+#include "ui/base/models/image_model.h"
#include "url/gurl.h"
namespace ash {
// The base item which is stored by the birch model.
struct ASH_EXPORT BirchItem {
- explicit BirchItem(const std::string& title);
+ BirchItem(const std::u16string& title, const ui::ImageModel icon);
BirchItem(BirchItem&&) = default;
BirchItem(const BirchItem&);
BirchItem& operator=(const BirchItem&);
~BirchItem();
bool operator==(const BirchItem& rhs) const = default;
- const std::string title;
+ const std::u16string title;
+ const ui::ImageModel icon;
};
// A birch item which contains file path and time information.
@@ -32,8 +34,8 @@
BirchFileItem(const base::FilePath& file_path,
const std::optional<base::Time>& timestamp);
BirchFileItem(BirchFileItem&&) = default;
- BirchFileItem(const BirchFileItem&);
- BirchFileItem& operator=(const BirchFileItem&);
+ BirchFileItem(const BirchFileItem&) = delete;
+ BirchFileItem& operator=(const BirchFileItem&) = delete;
bool operator==(const BirchFileItem& rhs) const = default;
~BirchFileItem();
@@ -46,7 +48,7 @@
// A birch item which contains tab and session information.
struct ASH_EXPORT BirchTabItem : public BirchItem {
- BirchTabItem(const std::string& title,
+ BirchTabItem(const std::u16string& title,
const GURL& url,
const base::Time& timestamp,
const GURL& favicon_url,
@@ -66,6 +68,22 @@
std::string ToString() const;
};
+struct ASH_EXPORT BirchWeatherItem : public BirchItem {
+ BirchWeatherItem(const std::u16string& weather_description,
+ const std::u16string& temperature,
+ ui::ImageModel icon);
+ BirchWeatherItem(BirchWeatherItem&&) = default;
+ BirchWeatherItem(const BirchWeatherItem&) = delete;
+ BirchWeatherItem& operator=(const BirchWeatherItem&) = delete;
+ bool operator==(const BirchWeatherItem& rhs) const = default;
+ ~BirchWeatherItem();
+
+ const std::u16string temperature;
+
+ // Intended for debugging.
+ std::string ToString() const;
+};
+
} // namespace ash
#endif // ASH_BIRCH_BIRCH_ITEM_H_
diff --git a/ash/birch/birch_model.cc b/ash/birch/birch_model.cc
index b388f63..9f9ed535 100644
--- a/ash/birch/birch_model.cc
+++ b/ash/birch/birch_model.cc
@@ -4,9 +4,16 @@
#include "ash/birch/birch_model.h"
+#include "ash/birch/birch_weather_provider.h"
+#include "ash/constants/ash_features.h"
+
namespace ash {
-BirchModel::BirchModel() = default;
+BirchModel::BirchModel() {
+ if (features::IsBirchWeatherEnabled()) {
+ weather_provider_ = std::make_unique<BirchWeatherProvider>(this);
+ }
+}
BirchModel::~BirchModel() = default;
@@ -39,6 +46,13 @@
}
recent_tab_items_ = std::move(recent_tab_items);
+}
+
+void BirchModel::SetWeatherItems(std::vector<BirchWeatherItem> weather_items) {
+ if (weather_items == weather_items_) {
+ return;
+ }
+ weather_items_ = std::move(weather_items);
for (auto& observer : observers_) {
observer.OnItemsChanged();
@@ -50,6 +64,9 @@
if (birch_client_) {
birch_client_->RequestBirchDataFetch();
}
+ if (weather_provider_) {
+ weather_provider_->RequestDataFetch();
+ }
}
} // namespace ash
diff --git a/ash/birch/birch_model.h b/ash/birch/birch_model.h
index 9589e36..de6c9fd 100644
--- a/ash/birch/birch_model.h
+++ b/ash/birch/birch_model.h
@@ -5,6 +5,9 @@
#ifndef ASH_BIRCH_BIRCH_MODEL_H_
#define ASH_BIRCH_BIRCH_MODEL_H_
+#include <optional>
+#include <vector>
+
#include "ash/ash_export.h"
#include "ash/birch/birch_client.h"
#include "ash/birch/birch_item.h"
@@ -12,6 +15,8 @@
namespace ash {
+class BirchWeatherProvider;
+
// Birch model, which is used to aggregate and store relevant information from
// different providers.
class ASH_EXPORT BirchModel {
@@ -39,8 +44,8 @@
void RequestBirchDataFetch();
void SetFileSuggestItems(std::vector<BirchFileItem> file_suggest_items);
-
void SetRecentTabItems(std::vector<BirchTabItem> recent_tab_items);
+ void SetWeatherItems(std::vector<BirchWeatherItem> weather_items);
void SetClient(BirchClient* client) { birch_client_ = client; }
@@ -51,6 +56,10 @@
return recent_tab_items_;
}
+ const std::vector<BirchWeatherItem>& GetWeatherForTest() const {
+ return weather_items_;
+ }
+
private:
// A type-specific list of items for all file suggestion items.
std::vector<BirchFileItem> file_suggest_items_;
@@ -58,8 +67,13 @@
// A type-specific list of items for all tab items.
std::vector<BirchTabItem> recent_tab_items_;
+ // A type-specific list of weather items.
+ std::vector<BirchWeatherItem> weather_items_;
+
raw_ptr<BirchClient> birch_client_ = nullptr;
+ std::unique_ptr<BirchWeatherProvider> weather_provider_;
+
base::ObserverList<Observer> observers_;
};
diff --git a/ash/birch/birch_weather_provider.cc b/ash/birch/birch_weather_provider.cc
new file mode 100644
index 0000000..db89a19
--- /dev/null
+++ b/ash/birch/birch_weather_provider.cc
@@ -0,0 +1,124 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/birch/birch_weather_provider.h"
+
+#include <string>
+
+#include "ash/ambient/ambient_controller.h"
+#include "ash/birch/birch_item.h"
+#include "ash/birch/birch_model.h"
+#include "ash/public/cpp/ambient/ambient_backend_controller.h"
+#include "ash/public/cpp/image_downloader.h"
+#include "ash/public/cpp/session/session_types.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "base/check.h"
+#include "base/functional/bind.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace ash {
+
+namespace {
+
+constexpr net::NetworkTrafficAnnotationTag kWeatherIconTag =
+ net::DefineNetworkTrafficAnnotation("weather_icon", R"(
+ semantics {
+ sender: "Birch feature"
+ description:
+ "Download weather icon image from Google."
+ trigger:
+ "The user opens an UI surface associated with birch feature."
+ data: "None."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "This feature is off by default."
+ policy_exception_justification:
+ "Policy is planned, but not yet implemented."
+ })");
+
+void DownloadImageFromUrl(const std::string& url_string,
+ ImageDownloader::DownloadCallback callback) {
+ GURL url(url_string);
+ if (!url.is_valid()) {
+ std::move(callback).Run(gfx::ImageSkia());
+ return;
+ }
+
+ const UserSession* active_user_session =
+ Shell::Get()->session_controller()->GetUserSession(0);
+ DCHECK(active_user_session);
+
+ ImageDownloader::Get()->Download(url, kWeatherIconTag,
+ active_user_session->user_info.account_id,
+ std::move(callback));
+}
+
+} // namespace
+
+BirchWeatherProvider::BirchWeatherProvider(BirchModel* birch_model)
+ : birch_model_(birch_model) {}
+
+BirchWeatherProvider::~BirchWeatherProvider() = default;
+
+void BirchWeatherProvider::RequestDataFetch() {
+ Shell::Get()
+ ->ambient_controller()
+ ->ambient_backend_controller()
+ ->FetchWeather(base::BindOnce(&BirchWeatherProvider::OnWeatherInfoFetched,
+ weak_factory_.GetWeakPtr()));
+}
+
+void BirchWeatherProvider::OnWeatherInfoFetched(
+ const std::optional<WeatherInfo>& weather_info) {
+ if (!weather_info || !weather_info->temp_f.has_value() ||
+ !weather_info->condition_icon_url ||
+ !weather_info->condition_description ||
+ weather_info->condition_icon_url->empty()) {
+ birch_model_->SetWeatherItems({});
+ 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.
+ DownloadImageFromUrl(
+ *weather_info->condition_icon_url,
+ base::BindOnce(&BirchWeatherProvider::OnWeatherConditionIconDownloaded,
+ weak_factory_.GetWeakPtr(),
+ base::UTF8ToUTF16(*weather_info->condition_description),
+ *weather_info->temp_f, weather_info->show_celsius));
+}
+
+void BirchWeatherProvider::OnWeatherConditionIconDownloaded(
+ const std::u16string& weather_description,
+ float temp_f,
+ bool show_celsius,
+ const gfx::ImageSkia& icon) {
+ if (icon.isNull()) {
+ birch_model_->SetWeatherItems({});
+ return;
+ }
+
+ std::u16string temperature_string =
+ show_celsius ? l10n_util::GetStringFUTF16Int(
+ IDS_ASH_AMBIENT_MODE_WEATHER_TEMPERATURE_IN_CELSIUS,
+ static_cast<int>((temp_f - 32) * 5 / 9))
+ : l10n_util::GetStringFUTF16Int(
+ IDS_ASH_AMBIENT_MODE_WEATHER_TEMPERATURE_IN_FAHRENHEIT,
+ static_cast<int>(temp_f));
+
+ std::vector<BirchWeatherItem> items;
+ items.emplace_back(weather_description, temperature_string,
+ ui::ImageModel::FromImageSkia(icon));
+ birch_model_->SetWeatherItems(std::move(items));
+}
+
+} // namespace ash
diff --git a/ash/birch/birch_weather_provider.h b/ash/birch/birch_weather_provider.h
new file mode 100644
index 0000000..712e0e2
--- /dev/null
+++ b/ash/birch/birch_weather_provider.h
@@ -0,0 +1,54 @@
+// Copyright 2024 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_BIRCH_BIRCH_WEATHER_PROVIDER_H_
+#define ASH_BIRCH_BIRCH_WEATHER_PROVIDER_H_
+
+#include <optional>
+
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+
+namespace gfx {
+class ImageSkia;
+}
+
+namespace ash {
+
+class BirchModel;
+
+struct WeatherInfo;
+
+class BirchWeatherProvider {
+ public:
+ explicit BirchWeatherProvider(BirchModel* birch_model);
+ BirchWeatherProvider(const BirchWeatherProvider&) = delete;
+ BirchWeatherProvider& operator=(const BirchWeatherProvider&) = delete;
+ ~BirchWeatherProvider();
+
+ // Called from birch model to request weather information to be displayed in
+ // UI.
+ void RequestDataFetch();
+
+ private:
+ // Called in response to a weather info request. It initiates icon fetch from
+ // the URL provided in the weather info.
+ void OnWeatherInfoFetched(const std::optional<WeatherInfo>& weather_info);
+
+ // Callback to weather info icon request. It will update birch model with the
+ // fetched weather info (including the downloaded weather icon).
+ void OnWeatherConditionIconDownloaded(
+ const std::u16string& weather_description,
+ float temp_f,
+ bool show_celsius,
+ const gfx::ImageSkia& icon);
+
+ const raw_ptr<BirchModel> birch_model_;
+
+ base::WeakPtrFactory<BirchWeatherProvider> weak_factory_{this};
+};
+
+} // namespace ash
+
+#endif // ASH_BIRCH_BIRCH_WEATHER_PROVIDER_H_
diff --git a/ash/birch/birch_weather_provider_unittest.cc b/ash/birch/birch_weather_provider_unittest.cc
new file mode 100644
index 0000000..d780a98e
--- /dev/null
+++ b/ash/birch/birch_weather_provider_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/birch/birch_weather_provider.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "ash/ambient/ambient_controller.h"
+#include "ash/birch/birch_model.h"
+#include "ash/constants/ash_features.h"
+#include "ash/constants/ash_switches.h"
+#include "ash/public/cpp/ambient/ambient_backend_controller.h"
+#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
+#include "ash/public/cpp/test/test_image_downloader.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
+
+namespace ash {
+
+class BirchWeatherProviderTest : public AshTestBase {
+ public:
+ BirchWeatherProviderTest() {
+ switches::SetIgnoreForestSecretKeyForTest(true);
+ feature_list_.InitWithFeatures(
+ {features::kForestFeature, features::kBirchWeather}, {});
+ }
+ ~BirchWeatherProviderTest() override {
+ switches::SetIgnoreForestSecretKeyForTest(false);
+ }
+
+ // AshTestBase:
+ void SetUp() override {
+ AshTestBase::SetUp();
+
+ image_downloader_ = std::make_unique<ash::TestImageDownloader>();
+
+ Shell::Get()->ambient_controller()->set_backend_controller_for_testing(
+ nullptr);
+ auto ambient_backend_controller =
+ std::make_unique<FakeAmbientBackendControllerImpl>();
+ ambient_backend_controller_ = ambient_backend_controller.get();
+ Shell::Get()->ambient_controller()->set_backend_controller_for_testing(
+ std::move(ambient_backend_controller));
+ }
+ void TearDown() override {
+ ambient_backend_controller_ = nullptr;
+ image_downloader_.reset();
+ AshTestBase::TearDown();
+ }
+
+ raw_ptr<FakeAmbientBackendControllerImpl> ambient_backend_controller_;
+ std::unique_ptr<TestImageDownloader> image_downloader_;
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(BirchWeatherProviderTest, GetWeather) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_description = "Cloudy";
+ info.condition_icon_url = "https://fake-icon-url";
+ info.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(info);
+
+ birch_model->RequestBirchDataFetch();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ auto& weather_items = birch_model->GetWeatherForTest();
+ ASSERT_EQ(1u, weather_items.size());
+ EXPECT_EQ(u"Cloudy", weather_items[0].title);
+ EXPECT_EQ(u"70\xB0 F", weather_items[0].temperature);
+ EXPECT_FALSE(weather_items[0].icon.IsEmpty());
+}
+
+TEST_F(BirchWeatherProviderTest, GetWeatherInCelsius) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_description = "Cloudy";
+ info.condition_icon_url = "https://fake-icon-url";
+ info.temp_f = 70.0f;
+ info.show_celsius = true;
+ ambient_backend_controller_->SetWeatherInfo(std::move(info));
+
+ birch_model->RequestBirchDataFetch();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ auto& weather_items = birch_model->GetWeatherForTest();
+ ASSERT_EQ(1u, weather_items.size());
+ EXPECT_EQ(u"Cloudy", weather_items[0].title);
+ EXPECT_EQ(u"21\xB0 C", weather_items[0].temperature);
+ EXPECT_FALSE(weather_items[0].icon.IsEmpty());
+}
+
+TEST_F(BirchWeatherProviderTest, NoWeatherInfo) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+TEST_F(BirchWeatherProviderTest, WeatherWithNoIcon) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_description = "Cloudy";
+ info.show_celsius = false;
+ info.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(std::move(info));
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+TEST_F(BirchWeatherProviderTest, WeatherWithInvalidIcon) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_description = "Cloudy";
+ info.condition_icon_url = "<invalid url>";
+ info.show_celsius = false;
+ info.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(std::move(info));
+
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+TEST_F(BirchWeatherProviderTest, WeatherIconDownloadFailure) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_description = "Cloudy";
+ info.condition_icon_url = "https://fake_icon_url";
+ info.show_celsius = false;
+ info.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(std::move(info));
+
+ image_downloader_->set_should_fail(true);
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+TEST_F(BirchWeatherProviderTest, WeatherWithNoTemperature) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_description = "Cloudy";
+ info.condition_icon_url = "https://fake_icon_url";
+ info.show_celsius = false;
+ ambient_backend_controller_->SetWeatherInfo(std::move(info));
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+TEST_F(BirchWeatherProviderTest, WeatherWithNoDecription) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info;
+ info.condition_icon_url = "https://fake_icon_url";
+ info.show_celsius = false;
+ info.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(std::move(info));
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+TEST_F(BirchWeatherProviderTest, RefetchWeather) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info1;
+ info1.condition_description = "Cloudy";
+ info1.condition_icon_url = "https://fake-icon-url";
+ info1.show_celsius = false;
+ info1.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(info1);
+
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ auto& weather_items = birch_model->GetWeatherForTest();
+ ASSERT_EQ(1u, weather_items.size());
+ EXPECT_EQ(u"Cloudy", weather_items[0].title);
+ EXPECT_EQ(u"70\xB0 F", weather_items[0].temperature);
+ EXPECT_FALSE(weather_items[0].icon.IsEmpty());
+
+ WeatherInfo info2;
+ info2.condition_description = "Sunny";
+ info2.condition_icon_url = "https://fake-icon-url";
+ info2.show_celsius = false;
+ info2.temp_f = 73.0f;
+ ambient_backend_controller_->SetWeatherInfo(info2);
+
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ auto& updated_weather_items = birch_model->GetWeatherForTest();
+ ASSERT_EQ(1u, updated_weather_items.size());
+ EXPECT_EQ(u"Sunny", updated_weather_items[0].title);
+ EXPECT_EQ(u"73\xB0 F", updated_weather_items[0].temperature);
+ EXPECT_FALSE(updated_weather_items[0].icon.IsEmpty());
+}
+
+TEST_F(BirchWeatherProviderTest, RefetchInvalidWeather) {
+ auto* birch_model = Shell::Get()->birch_model();
+
+ WeatherInfo info1;
+ info1.condition_description = "Cloudy";
+ info1.condition_icon_url = "https://fake-icon-url";
+ info1.show_celsius = false;
+ info1.temp_f = 70.0f;
+ ambient_backend_controller_->SetWeatherInfo(info1);
+
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+
+ auto& weather_items = birch_model->GetWeatherForTest();
+ ASSERT_EQ(1u, weather_items.size());
+ EXPECT_EQ(u"Cloudy", weather_items[0].title);
+ EXPECT_EQ(u"70\xB0 F", weather_items[0].temperature);
+ EXPECT_FALSE(weather_items[0].icon.IsEmpty());
+
+ WeatherInfo info2;
+ info2.show_celsius = false;
+ ambient_backend_controller_->SetWeatherInfo(info2);
+
+ birch_model->RequestBirchDataFetch();
+
+ // The fake image downloader post a task to respond with an image.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(birch_model->GetWeatherForTest().empty());
+}
+
+} // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 509217fb..7b0089d2 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -286,6 +286,9 @@
"CrosBatterySaverAlwaysOn",
base::FEATURE_DISABLED_BY_DEFAULT);
+// Display weather information in birch UI.
+BASE_FEATURE(kBirchWeather, "BirchWeather", base::FEATURE_DISABLED_BY_DEFAULT);
+
// Enables or disables the usage of fixed Bluetooth A2DP packet size to improve
// audio performance in noisy environment.
BASE_FEATURE(kBluetoothFixA2dpPacketSize,
@@ -3192,6 +3195,10 @@
return base::FeatureList::IsEnabled(kBatterySaverAlwaysOn);
}
+bool IsBirchWeatherEnabled() {
+ return base::FeatureList::IsEnabled(kBirchWeather);
+}
+
bool IsBluetoothDisconnectWarningEnabled() {
return base::FeatureList::IsEnabled(kBluetoothDisconnectWarning);
}
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 7f84678..3f92e95 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -75,6 +75,7 @@
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kAvatarsCloudMigration);
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBackgroundListening);
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBatterySaver);
+COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBirchWeather);
enum BatterySaverNotificationBehavior { kBSMAutoEnable, kBSMOptIn };
COMPONENT_EXPORT(ASH_CONSTANTS)
extern const base::FeatureParam<BatterySaverNotificationBehavior>
@@ -934,6 +935,7 @@
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBackgroundListeningEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBatterySaverAvailable();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBatterySaverAlwaysOn();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBirchWeatherEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBluetoothQualityReportEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBluetoothDisconnectWarningEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCaptureModeAudioMixingEnabled();
diff --git a/ash/public/cpp/ambient/ambient_backend_controller.h b/ash/public/cpp/ambient/ambient_backend_controller.h
index b9f3ba52..70102ea 100644
--- a/ash/public/cpp/ambient/ambient_backend_controller.h
+++ b/ash/public/cpp/ambient/ambient_backend_controller.h
@@ -58,6 +58,9 @@
WeatherInfo& operator=(const WeatherInfo&);
~WeatherInfo();
+ // The description of the weather condition.
+ std::optional<std::string> condition_description;
+
// The url of the weather condition icon image.
std::optional<std::string> condition_icon_url;
diff --git a/chrome/browser/ui/ash/birch/birch_keyed_service_unittest.cc b/chrome/browser/ui/ash/birch/birch_keyed_service_unittest.cc
index 29c4e90..a26788c 100644
--- a/chrome/browser/ui/ash/birch/birch_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/birch/birch_keyed_service_unittest.cc
@@ -329,11 +329,11 @@
auto& tabs = Shell::Get()->birch_model()->GetTabsForTest();
ASSERT_EQ(tabs.size(), 2u);
- EXPECT_EQ(tabs[0].title, base::UTF16ToUTF8(kTabTitle1));
+ EXPECT_EQ(tabs[0].title, kTabTitle1);
EXPECT_EQ(tabs[0].url, GURL(kExampleURL1));
EXPECT_EQ(tabs[0].session_name, kSessionName1);
- EXPECT_EQ(tabs[1].title, base::UTF16ToUTF8(kTabTitle2));
+ EXPECT_EQ(tabs[1].title, kTabTitle2);
EXPECT_EQ(tabs[1].url, GURL(kExampleURL2));
EXPECT_EQ(tabs[1].session_name, kSessionName2);
}
diff --git a/chrome/browser/ui/ash/birch/birch_recent_tabs_provider.cc b/chrome/browser/ui/ash/birch/birch_recent_tabs_provider.cc
index ca7074c..7d868c7 100644
--- a/chrome/browser/ui/ash/birch/birch_recent_tabs_provider.cc
+++ b/chrome/browser/ui/ash/birch/birch_recent_tabs_provider.cc
@@ -44,9 +44,9 @@
const sessions::SerializedNavigationEntry& current_navigation =
tab->navigations.at(tab->normalized_navigation_index());
items.emplace_back(
- base::UTF16ToUTF8(current_navigation.title()),
- current_navigation.virtual_url(), current_navigation.timestamp(),
- current_navigation.favicon_url(), session->GetSessionName());
+ current_navigation.title(), current_navigation.virtual_url(),
+ current_navigation.timestamp(), current_navigation.favicon_url(),
+ session->GetSessionName());
}
}
}