Commit 70bcb243 authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS PhoneHub] Create BrowserTabsModel and add to PhoneModel

BrowserTabsModel contains metadata about the two most-recently-used
browser tabs on the user's phone. This class is now integrated with the
high-level PhoneModel class.

There is still no code to set the fields within this new model; this
will come in a future CL.

Bug: 1106937
Change-Id: I972aed6c69de33dc733cf52686d660e9465030cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2368075
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarRegan Hsu <hsuregan@chromium.org>
Auto-Submit: Kyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800587}
parent 7e6501db
...@@ -8,6 +8,8 @@ assert(is_chromeos, "Phone Hub is Chrome OS only") ...@@ -8,6 +8,8 @@ assert(is_chromeos, "Phone Hub is Chrome OS only")
static_library("phonehub") { static_library("phonehub") {
sources = [ sources = [
"browser_tabs_model.cc",
"browser_tabs_model.h",
"feature_status.cc", "feature_status.cc",
"feature_status.h", "feature_status.h",
"feature_status_provider.cc", "feature_status_provider.cc",
...@@ -41,6 +43,8 @@ static_library("phonehub") { ...@@ -41,6 +43,8 @@ static_library("phonehub") {
"//components/keyed_service/core", "//components/keyed_service/core",
"//components/prefs", "//components/prefs",
"//device/bluetooth", "//device/bluetooth",
"//ui/gfx",
"//url",
] ]
} }
...@@ -65,6 +69,7 @@ source_set("unit_tests") { ...@@ -65,6 +69,7 @@ source_set("unit_tests") {
testonly = true testonly = true
sources = [ sources = [
"browser_tabs_model_unittest.cc",
"feature_status_provider_impl_unittest.cc", "feature_status_provider_impl_unittest.cc",
"mutable_phone_model_unittest.cc", "mutable_phone_model_unittest.cc",
"notification_access_manager_impl_unittest.cc", "notification_access_manager_impl_unittest.cc",
......
...@@ -5,4 +5,5 @@ include_rules = [ ...@@ -5,4 +5,5 @@ include_rules = [
"+components/keyed_service/core/keyed_service.h", "+components/keyed_service/core/keyed_service.h",
"+components/prefs", "+components/prefs",
"+device/bluetooth", "+device/bluetooth",
"+ui/gfx",
] ]
// Copyright 2020 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 "chromeos/components/phonehub/browser_tabs_model.h"
#include "chromeos/components/multidevice/logging/logging.h"
namespace chromeos {
namespace phonehub {
BrowserTabsModel::BrowserTabMetadata::BrowserTabMetadata(
GURL url,
const base::string16& title,
base::Time last_accessed_timestamp,
const gfx::Image& favicon)
: url(url),
title(title),
last_accessed_timestamp(last_accessed_timestamp),
favicon(favicon) {}
BrowserTabsModel::BrowserTabMetadata::BrowserTabMetadata(
const BrowserTabMetadata& other) = default;
bool BrowserTabsModel::BrowserTabMetadata::operator==(
const BrowserTabMetadata& other) const {
return url == other.url && title == other.title &&
last_accessed_timestamp == other.last_accessed_timestamp &&
favicon == other.favicon;
}
bool BrowserTabsModel::BrowserTabMetadata::operator!=(
const BrowserTabMetadata& other) const {
return !(*this == other);
}
BrowserTabsModel::BrowserTabsModel(
bool is_tab_sync_enabled,
const base::Optional<BrowserTabMetadata>& most_recent_tab,
const base::Optional<BrowserTabMetadata>& second_most_recent_tab)
: is_tab_sync_enabled_(is_tab_sync_enabled),
most_recent_tab_(most_recent_tab),
second_most_recent_tab_(second_most_recent_tab) {
if (!is_tab_sync_enabled_ &&
(most_recent_tab_.has_value() || second_most_recent_tab_.has_value())) {
PA_LOG(WARNING) << "Tab sync is not enabled, but tab metadata was "
<< "provided; clearing metadata.";
most_recent_tab_.reset();
second_most_recent_tab_.reset();
}
}
BrowserTabsModel::BrowserTabsModel(const BrowserTabsModel& other) = default;
BrowserTabsModel::~BrowserTabsModel() = default;
bool BrowserTabsModel::operator==(const BrowserTabsModel& other) const {
return is_tab_sync_enabled_ == other.is_tab_sync_enabled_ &&
most_recent_tab_ == other.most_recent_tab_ &&
second_most_recent_tab_ == other.second_most_recent_tab_;
}
bool BrowserTabsModel::operator!=(const BrowserTabsModel& other) const {
return !(*this == other);
}
std::ostream& operator<<(
std::ostream& stream,
BrowserTabsModel::BrowserTabMetadata browser_tab_metadata) {
stream << "{URL: " << browser_tab_metadata.url.spec() << ", "
<< "Title: " << browser_tab_metadata.title << ", "
<< "Timestamp: " << browser_tab_metadata.last_accessed_timestamp
<< "}";
return stream;
}
} // namespace phonehub
} // namespace chromeos
// Copyright 2020 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 CHROMEOS_COMPONENTS_PHONEHUB_BROWSER_TABS_MODEL_H_
#define CHROMEOS_COMPONENTS_PHONEHUB_BROWSER_TABS_MODEL_H_
#include <ostream>
#include "base/optional.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
namespace chromeos {
namespace phonehub {
// Contains metadata about browser tabs that are open on the user's phone.
class BrowserTabsModel {
public:
struct BrowserTabMetadata {
BrowserTabMetadata(GURL url,
const base::string16& title,
base::Time last_accessed_timestamp,
const gfx::Image& favicon);
BrowserTabMetadata(const BrowserTabMetadata& other);
bool operator==(const BrowserTabMetadata& other) const;
bool operator!=(const BrowserTabMetadata& other) const;
GURL url;
base::string16 title;
base::Time last_accessed_timestamp;
gfx::Image favicon;
};
// |is_tab_sync_enabled| indicates whether the Chrome OS device is currently
// syncing tab metadata. If that parameter is false, the optional tab
// parameters should be null. If it is true, one or both of the parameters may
// still be null if the user does not have browser tabs open on their phone.
BrowserTabsModel(
bool is_tab_sync_enabled,
const base::Optional<BrowserTabMetadata>& most_recent_tab = base::nullopt,
const base::Optional<BrowserTabMetadata>& second_most_recent_tab =
base::nullopt);
BrowserTabsModel(const BrowserTabsModel& other);
~BrowserTabsModel();
bool operator==(const BrowserTabsModel& other) const;
bool operator!=(const BrowserTabsModel& other) const;
bool is_tab_sync_enabled() const { return is_tab_sync_enabled_; }
const base::Optional<BrowserTabMetadata>& most_recent_tab() const {
return most_recent_tab_;
}
const base::Optional<BrowserTabMetadata>& second_most_recent_tab() const {
return second_most_recent_tab_;
}
private:
bool is_tab_sync_enabled_;
base::Optional<BrowserTabMetadata> most_recent_tab_;
base::Optional<BrowserTabMetadata> second_most_recent_tab_;
};
std::ostream& operator<<(
std::ostream& stream,
BrowserTabsModel::BrowserTabMetadata browser_tab_metadata);
} // namespace phonehub
} // namespace chromeos
#endif // CHROMEOS_COMPONENTS_PHONEHUB_BROWSER_TABS_MODEL_H_
// Copyright 2020 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 "chromeos/components/phonehub/browser_tabs_model.h"
#include "chromeos/components/phonehub/phone_model_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace phonehub {
TEST(BrowserTabsModelTest, Initialization) {
BrowserTabsModel success(/*is_tab_sync_enabled=*/true,
CreateFakeBrowserTabMetadata());
EXPECT_TRUE(success.is_tab_sync_enabled());
EXPECT_EQ(CreateFakeBrowserTabMetadata(), *success.most_recent_tab());
EXPECT_FALSE(success.second_most_recent_tab().has_value());
// If tab metadata is provided by tab sync is disabled, the data should be
// cleared.
BrowserTabsModel invalid_metadata(/*is_tab_sync_enabled=*/false,
CreateFakeBrowserTabMetadata());
EXPECT_FALSE(invalid_metadata.most_recent_tab().has_value());
}
} // namespace phonehub
} // namespace chromeos
...@@ -20,5 +20,14 @@ void MutablePhoneModel::SetPhoneStatusModel( ...@@ -20,5 +20,14 @@ void MutablePhoneModel::SetPhoneStatusModel(
NotifyModelChanged(); NotifyModelChanged();
} }
void MutablePhoneModel::SetBrowserTabsModel(
const base::Optional<BrowserTabsModel>& browser_tabs_model) {
if (browser_tabs_model_ == browser_tabs_model)
return;
browser_tabs_model_ = browser_tabs_model;
NotifyModelChanged();
}
} // namespace phonehub } // namespace phonehub
} // namespace chromeos } // namespace chromeos
...@@ -19,6 +19,8 @@ class MutablePhoneModel : public PhoneModel { ...@@ -19,6 +19,8 @@ class MutablePhoneModel : public PhoneModel {
void SetPhoneStatusModel( void SetPhoneStatusModel(
const base::Optional<PhoneStatusModel>& phone_status_model); const base::Optional<PhoneStatusModel>& phone_status_model);
void SetBrowserTabsModel(
const base::Optional<BrowserTabsModel>& browser_tabs_model);
}; };
} // namespace phonehub } // namespace phonehub
......
...@@ -71,5 +71,28 @@ TEST_F(MutablePhoneModelTest, PhoneStatusModel) { ...@@ -71,5 +71,28 @@ TEST_F(MutablePhoneModelTest, PhoneStatusModel) {
EXPECT_EQ(2u, GetNumObserverCalls()); EXPECT_EQ(2u, GetNumObserverCalls());
} }
TEST_F(MutablePhoneModelTest, BrowserTabsModel) {
// Set the BrowserTabsModel to be null (the default value); observers should
// not be notified, since this is not a change.
model_.SetBrowserTabsModel(/*browser_tabs_model=*/base::nullopt);
EXPECT_FALSE(model_.browser_tabs_model().has_value());
EXPECT_EQ(0u, GetNumObserverCalls());
// Set the BrowserTabsModel; observers should be notified.
model_.SetBrowserTabsModel(CreateFakeBrowserTabsModel());
EXPECT_EQ(CreateFakeBrowserTabsModel(), model_.browser_tabs_model());
EXPECT_EQ(1u, GetNumObserverCalls());
// Set the same BrowserTabsModel; observers should not be notified.
model_.SetBrowserTabsModel(CreateFakeBrowserTabsModel());
EXPECT_EQ(CreateFakeBrowserTabsModel(), model_.browser_tabs_model());
EXPECT_EQ(1u, GetNumObserverCalls());
// Set the BrowserTabsModel back to null; observers should be notified.
model_.SetBrowserTabsModel(/*browser_tabs_model=*/base::nullopt);
EXPECT_FALSE(model_.browser_tabs_model().has_value());
EXPECT_EQ(2u, GetNumObserverCalls());
}
} // namespace phonehub } // namespace phonehub
} // namespace chromeos } // namespace chromeos
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/observer_list_types.h" #include "base/observer_list_types.h"
#include "base/optional.h" #include "base/optional.h"
#include "chromeos/components/phonehub/browser_tabs_model.h"
#include "chromeos/components/phonehub/phone_status_model.h" #include "chromeos/components/phonehub/phone_status_model.h"
namespace chromeos { namespace chromeos {
...@@ -17,7 +18,6 @@ namespace phonehub { ...@@ -17,7 +18,6 @@ namespace phonehub {
// return the state of the phone when connected, or null if disconnected. Also // return the state of the phone when connected, or null if disconnected. Also
// exposes an observer interface so that clients can be notified of changes to // exposes an observer interface so that clients can be notified of changes to
// the model. // the model.
// TODO(khorimoto): Add additional metadata to this model.
class PhoneModel { class PhoneModel {
public: public:
class Observer : public base::CheckedObserver { class Observer : public base::CheckedObserver {
...@@ -36,6 +36,10 @@ class PhoneModel { ...@@ -36,6 +36,10 @@ class PhoneModel {
return phone_status_model_; return phone_status_model_;
} }
const base::Optional<BrowserTabsModel>& browser_tabs_model() const {
return browser_tabs_model_;
}
void AddObserver(Observer* observer); void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer); void RemoveObserver(Observer* observer);
...@@ -45,6 +49,7 @@ class PhoneModel { ...@@ -45,6 +49,7 @@ class PhoneModel {
void NotifyModelChanged(); void NotifyModelChanged();
base::Optional<PhoneStatusModel> phone_status_model_; base::Optional<PhoneStatusModel> phone_status_model_;
base::Optional<BrowserTabsModel> browser_tabs_model_;
private: private:
base::ObserverList<Observer> observer_list_; base::ObserverList<Observer> observer_list_;
......
...@@ -12,6 +12,12 @@ namespace phonehub { ...@@ -12,6 +12,12 @@ namespace phonehub {
const char kFakeMobileProviderName[] = "Fake Mobile Provider"; const char kFakeMobileProviderName[] = "Fake Mobile Provider";
const char kFakeBrowserTabUrl1[] = "https://www.example.com/tab1";
const char kFakeBrowserTabName1[] = "Tab 1";
const char kFakeBrowserTabUrl2[] = "https://www.example.com/tab2";
const char kFakeBrowserTabName2[] = "Tab 2";
const PhoneStatusModel::MobileConnectionMetadata& const PhoneStatusModel::MobileConnectionMetadata&
CreateFakeMobileConnectionMetadata() { CreateFakeMobileConnectionMetadata() {
static const base::NoDestructor<PhoneStatusModel::MobileConnectionMetadata> static const base::NoDestructor<PhoneStatusModel::MobileConnectionMetadata>
...@@ -22,13 +28,34 @@ CreateFakeMobileConnectionMetadata() { ...@@ -22,13 +28,34 @@ CreateFakeMobileConnectionMetadata() {
} }
const PhoneStatusModel& CreateFakePhoneStatusModel() { const PhoneStatusModel& CreateFakePhoneStatusModel() {
static const base::NoDestructor<PhoneStatusModel> fake_status_model{ static const base::NoDestructor<PhoneStatusModel> fake_phone_status_model{
PhoneStatusModel::MobileStatus::kSimWithReception, PhoneStatusModel::MobileStatus::kSimWithReception,
CreateFakeMobileConnectionMetadata(), CreateFakeMobileConnectionMetadata(),
PhoneStatusModel::ChargingState::kNotCharging, PhoneStatusModel::ChargingState::kNotCharging,
PhoneStatusModel::BatterySaverState::kOff, PhoneStatusModel::BatterySaverState::kOff,
/*battery_percentage=*/100u}; /*battery_percentage=*/100u};
return *fake_status_model; return *fake_phone_status_model;
}
const BrowserTabsModel::BrowserTabMetadata& CreateFakeBrowserTabMetadata() {
static const base::NoDestructor<BrowserTabsModel::BrowserTabMetadata>
fake_browser_tab_metadata{GURL(kFakeBrowserTabUrl1),
base::UTF8ToUTF16(kFakeBrowserTabName1),
base::Time(), gfx::Image()};
return *fake_browser_tab_metadata;
}
const BrowserTabsModel& CreateFakeBrowserTabsModel() {
static const base::NoDestructor<BrowserTabsModel::BrowserTabMetadata>
second_browser_tab_metadata{GURL(kFakeBrowserTabUrl2),
base::UTF8ToUTF16(kFakeBrowserTabName2),
base::Time(), gfx::Image()};
static const base::NoDestructor<BrowserTabsModel> fake_browser_tabs_model{
/*is_tab_sync_enabled=*/true, CreateFakeBrowserTabMetadata(),
*second_browser_tab_metadata};
return *fake_browser_tabs_model;
} }
} // namespace phonehub } // namespace phonehub
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROMEOS_COMPONENTS_PHONEHUB_PHONE_MODEL_TEST_UTIL_H_ #ifndef CHROMEOS_COMPONENTS_PHONEHUB_PHONE_MODEL_TEST_UTIL_H_
#define CHROMEOS_COMPONENTS_PHONEHUB_PHONE_MODEL_TEST_UTIL_H_ #define CHROMEOS_COMPONENTS_PHONEHUB_PHONE_MODEL_TEST_UTIL_H_
#include "chromeos/components/phonehub/browser_tabs_model.h"
#include "chromeos/components/phonehub/phone_status_model.h" #include "chromeos/components/phonehub/phone_status_model.h"
namespace chromeos { namespace chromeos {
...@@ -12,11 +13,21 @@ namespace phonehub { ...@@ -12,11 +13,21 @@ namespace phonehub {
extern const char kFakeMobileProviderName[]; extern const char kFakeMobileProviderName[];
// Creates fake data for use in tests. extern const char kFakeBrowserTabUrl1[];
extern const char kFakeBrowserTabName1[];
extern const char kFakeBrowserTabUrl2[];
extern const char kFakeBrowserTabName2[];
// Creates fake phone status data for use in tests.
const PhoneStatusModel::MobileConnectionMetadata& const PhoneStatusModel::MobileConnectionMetadata&
CreateFakeMobileConnectionMetadata(); CreateFakeMobileConnectionMetadata();
const PhoneStatusModel& CreateFakePhoneStatusModel(); const PhoneStatusModel& CreateFakePhoneStatusModel();
// Creates fake browser tab data for use in tests.
const BrowserTabsModel::BrowserTabMetadata& CreateFakeBrowserTabMetadata();
const BrowserTabsModel& CreateFakeBrowserTabsModel();
} // namespace phonehub } // namespace phonehub
} // namespace chromeos } // namespace chromeos
......
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