From e1a6276ae0adda5618c6e2adae5be9768bdfb9a9 Mon Sep 17 00:00:00 2001 From: Kyle Horimoto Date: Fri, 11 Jan 2019 20:34:23 +0000 Subject: [PATCH] [CrOS MultiDevice] Show notification when Messages pairing is lost. Once multi-device setup is completed, the "Messages" PWA is installed, but further setup is required before the app can be used. Specifically, the Chromebook needs to be paired with the user's phone via a QR code displayed in the PWA. However, this pairing is broken the PWA is migrated from messages.android.com to messages.google.com (see https://crbug.com/917855. This CL alerts the user that the devices need to be re-paired when this situation arises. Bug: 918943 Change-Id: Ib662edf42ba01320cc12e6f3955b80c390d4493e Reviewed-on: https://chromium-review.googlesource.com/c/1406159 Commit-Queue: Kyle Horimoto Reviewed-by: Evan Stade Cr-Commit-Position: refs/heads/master@{#622126} --- ash/public/cpp/vector_icons/BUILD.gn | 1 + .../vector_icons/notification_messages.icon | 30 ++++ .../message_center_controller.cc | 1 + chrome/app/chromeos_strings.grdp | 8 + chrome/browser/chromeos/BUILD.gn | 3 + .../android_sms/android_sms_service.cc | 13 +- .../android_sms/android_sms_service.h | 9 +- .../android_sms_service_factory.cc | 10 ++ .../android_sms/android_sms_service_factory.h | 6 + .../android_sms/pairing_lost_notifier.cc | 150 ++++++++++++++++++ .../android_sms/pairing_lost_notifier.h | 66 ++++++++ .../pairing_lost_notifier_unittest.cc | 145 +++++++++++++++++ 12 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 ash/public/cpp/vector_icons/notification_messages.icon create mode 100644 chrome/browser/chromeos/android_sms/pairing_lost_notifier.cc create mode 100644 chrome/browser/chromeos/android_sms/pairing_lost_notifier.h create mode 100644 chrome/browser/chromeos/android_sms/pairing_lost_notifier_unittest.cc diff --git a/ash/public/cpp/vector_icons/BUILD.gn b/ash/public/cpp/vector_icons/BUILD.gn index 1cd52813916fc..89043550d3957 100644 --- a/ash/public/cpp/vector_icons/BUILD.gn +++ b/ash/public/cpp/vector_icons/BUILD.gn @@ -24,6 +24,7 @@ aggregate_vector_icons("ash_public_vector_icons") { "notification_image.icon", "notification_installed.icon", "notification_linux.icon", + "notification_messages.icon", "notification_mobile_data.icon", "notification_mobile_data_off.icon", "notification_multi_device_setup.icon", diff --git a/ash/public/cpp/vector_icons/notification_messages.icon b/ash/public/cpp/vector_icons/notification_messages.icon new file mode 100644 index 0000000000000..462248855859e --- /dev/null +++ b/ash/public/cpp/vector_icons/notification_messages.icon @@ -0,0 +1,30 @@ +// 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. + +CANVAS_DIMENSIONS, 36, +MOVE_TO, 31, 4, +H_LINE_TO, 5, +R_ARC_TO, 3, 3, 0, 0, 0, -2.98f, 3, +R_V_LINE_TO, 27, +LINE_TO, 10, 28, +R_H_LINE_TO, 21, +R_CUBIC_TO, 1.65f, 0, 3, -1.35f, 3, -3, +V_LINE_TO, 7, +R_CUBIC_TO, 0, -1.65f, -1.35f, -3, -3, -3, +CLOSE, +MOVE_TO, 9, 20, +R_H_LINE_TO, 12, +R_V_LINE_TO, 3, +H_LINE_TO, 9, +CLOSE, +R_MOVE_TO, 0, -6, +R_H_LINE_TO, 18, +R_V_LINE_TO, 3, +H_LINE_TO, 9, +CLOSE, +R_MOVE_TO, 0, -6, +R_H_LINE_TO, 18, +R_V_LINE_TO, 3, +H_LINE_TO, 9, +CLOSE diff --git a/ash/system/message_center/message_center_controller.cc b/ash/system/message_center/message_center_controller.cc index b2d1cc2e6dd2b..5229ae40e2503 100644 --- a/ash/system/message_center/message_center_controller.cc +++ b/ash/system/message_center/message_center_controller.cc @@ -136,6 +136,7 @@ MessageCenterController::MessageCenterController() { &kNotificationImageIcon, &kNotificationInstalledIcon, &kNotificationLinuxIcon, + &kNotificationMessagesIcon, &kNotificationMultiDeviceSetupIcon, &kNotificationMobileDataIcon, &kNotificationMobileDataOffIcon, diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 25b67d7258d36..ab804297ae8ff 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp @@ -181,6 +181,14 @@ Offer new features as they become available + + + Pair your phone with Messages + + + Send and receive text messages from your Chromebook + + Checking for updates diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index e884049953c17..c2d2b8d403781 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn @@ -329,6 +329,8 @@ source_set("chromeos") { "android_sms/connection_establisher_impl.h", "android_sms/connection_manager.cc", "android_sms/connection_manager.h", + "android_sms/pairing_lost_notifier.cc", + "android_sms/pairing_lost_notifier.h", "app_mode/app_launch_utils.cc", "app_mode/app_launch_utils.h", "app_mode/app_session.cc", @@ -2111,6 +2113,7 @@ source_set("unit_tests") { "android_sms/android_sms_app_helper_delegate_impl_unittest.cc", "android_sms/connection_establisher_impl_unittest.cc", "android_sms/connection_manager_unittest.cc", + "android_sms/pairing_lost_notifier_unittest.cc", "app_mode/startup_app_launcher_unittest.cc", "apps/apk_web_app_installer_unittest.cc", "apps/intent_helper/apps_navigation_throttle_unittest.cc", diff --git a/chrome/browser/chromeos/android_sms/android_sms_service.cc b/chrome/browser/chromeos/android_sms/android_sms_service.cc index 2458b3de550fe..ee663fa530374 100644 --- a/chrome/browser/chromeos/android_sms/android_sms_service.cc +++ b/chrome/browser/chromeos/android_sms/android_sms_service.cc @@ -8,6 +8,7 @@ #include "chrome/browser/chromeos/android_sms/android_sms_urls.h" #include "chrome/browser/chromeos/android_sms/connection_establisher_impl.h" #include "chrome/browser/chromeos/android_sms/connection_manager.h" +#include "chrome/browser/chromeos/android_sms/pairing_lost_notifier.h" #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/web_applications/web_app_provider.h" @@ -32,7 +33,12 @@ AndroidSmsService::AndroidSmsService( &web_app_provider->pending_app_manager(), host_content_settings_map)), android_sms_pairing_state_tracker_( - std::make_unique(profile_)) { + std::make_unique(profile_)), + pairing_lost_notifier_(std::make_unique( + profile, + multidevice_setup_client, + profile_->GetPrefs(), + android_sms_app_helper_delegate_.get())) { session_manager::SessionManager::Get()->AddObserver(this); } @@ -40,8 +46,11 @@ AndroidSmsService::~AndroidSmsService() = default; void AndroidSmsService::Shutdown() { connection_manager_.reset(); - android_sms_app_helper_delegate_.reset(); + // Note: |pairing_lost_notifier_| holds a reference to + // |android_sms_app_helper_delegate_|, so it should be deleted first. + pairing_lost_notifier_.reset(); android_sms_pairing_state_tracker_.reset(); + android_sms_app_helper_delegate_.reset(); session_manager::SessionManager::Get()->RemoveObserver(this); } diff --git a/chrome/browser/chromeos/android_sms/android_sms_service.h b/chrome/browser/chromeos/android_sms/android_sms_service.h index a842d39e56312..52f4c4f4489f7 100644 --- a/chrome/browser/chromeos/android_sms/android_sms_service.h +++ b/chrome/browser/chromeos/android_sms/android_sms_service.h @@ -29,12 +29,14 @@ class MultiDeviceSetupClient; namespace android_sms { class ConnectionManager; +class PairingLostNotifier; // KeyedService which manages Android Messages integration. This service -// has three main responsibilities: +// has four main responsibilities: // (1) Maintaining a connection with the Messages ServiceWorker, -// (2) Managing installation/launching of the Messages PWA, and -// (3) Tracking the pairing state of the PWA. +// (2) Managing installation/launching of the Messages PWA, +// (3) Tracking the pairing state of the PWA, and +// (4) Notifying users when their phones need to be re-paired. class AndroidSmsService : public KeyedService, public session_manager::SessionManagerObserver { public: @@ -69,6 +71,7 @@ class AndroidSmsService : public KeyedService, android_sms_app_helper_delegate_; std::unique_ptr android_sms_pairing_state_tracker_; + std::unique_ptr pairing_lost_notifier_; std::unique_ptr connection_manager_; DISALLOW_COPY_AND_ASSIGN(AndroidSmsService); diff --git a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc index 8e17a51c525b4..0b46c09d93c50 100644 --- a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc +++ b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc @@ -3,14 +3,18 @@ // found in the LICENSE file. #include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h" + +#include "chrome/browser/chromeos/android_sms/pairing_lost_notifier.h" #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/notifications/notification_display_service_factory.h" #include "chrome/browser/web_applications/web_app_provider_factory.h" #include "chromeos/constants/chromeos_features.h" #include "chromeos/services/multidevice_setup/public/cpp/prefs.h" #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/pref_registry/pref_registry_syncable.h" namespace chromeos { @@ -48,6 +52,7 @@ AndroidSmsServiceFactory::AndroidSmsServiceFactory() DependsOn(chromeos::multidevice_setup::MultiDeviceSetupClientFactory:: GetInstance()); DependsOn(web_app::WebAppProviderFactory::GetInstance()); + DependsOn(NotificationDisplayServiceFactory::GetInstance()); } AndroidSmsServiceFactory::~AndroidSmsServiceFactory() = default; @@ -79,6 +84,11 @@ bool AndroidSmsServiceFactory::ServiceIsNULLWhileTesting() const { return true; } +void AndroidSmsServiceFactory::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + PairingLostNotifier::RegisterProfilePrefs(registry); +} + } // namespace android_sms } // namespace chromeos diff --git a/chrome/browser/chromeos/android_sms/android_sms_service_factory.h b/chrome/browser/chromeos/android_sms/android_sms_service_factory.h index e2ba7b7411eb5..f584067bf1ebe 100644 --- a/chrome/browser/chromeos/android_sms/android_sms_service_factory.h +++ b/chrome/browser/chromeos/android_sms/android_sms_service_factory.h @@ -9,6 +9,10 @@ #include "chrome/browser/chromeos/android_sms/android_sms_service.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" +namespace user_prefs { +class PrefRegistrySyncable; +} // namespace user_prefs + namespace chromeos { namespace android_sms { @@ -31,6 +35,8 @@ class AndroidSmsServiceFactory : public BrowserContextKeyedServiceFactory { content::BrowserContext* context) const override; bool ServiceIsCreatedWithBrowserContext() const override; bool ServiceIsNULLWhileTesting() const override; + void RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) override; DISALLOW_COPY_AND_ASSIGN(AndroidSmsServiceFactory); }; diff --git a/chrome/browser/chromeos/android_sms/pairing_lost_notifier.cc b/chrome/browser/chromeos/android_sms/pairing_lost_notifier.cc new file mode 100644 index 0000000000000..2b7209b3ad387 --- /dev/null +++ b/chrome/browser/chromeos/android_sms/pairing_lost_notifier.cc @@ -0,0 +1,150 @@ +// 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. + +#include "chrome/browser/chromeos/android_sms/pairing_lost_notifier.h" + +#include "ash/public/cpp/notification_utils.h" +#include "ash/public/cpp/vector_icons/vector_icons.h" +#include "chrome/browser/notifications/notification_display_service.h" +#include "chrome/browser/ui/ash/multi_user/multi_user_util.h" +#include "chrome/grit/generated_resources.h" +#include "chromeos/components/multidevice/logging/logging.h" +#include "chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h" +#include "components/account_id/account_id.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/message_center/public/cpp/notification_types.h" + +namespace chromeos { + +namespace android_sms { + +namespace { + +const char kWasPreviouslySetUpPrefName[] = "android_sms.was_previously_set_up"; + +const char kAndroidSmsNotifierId[] = "ash.android_sms"; +const char kPairingLostNotificationId[] = "android_sms.pairing_lost"; + +} // namespace + +// static +void PairingLostNotifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(kWasPreviouslySetUpPrefName, false); +} + +PairingLostNotifier::PairingLostNotifier( + Profile* profile, + multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client, + PrefService* pref_service, + multidevice_setup::AndroidSmsAppHelperDelegate* + android_sms_app_helper_delegate) + : profile_(profile), + multidevice_setup_client_(multidevice_setup_client), + pref_service_(pref_service), + android_sms_app_helper_delegate_(android_sms_app_helper_delegate), + weak_ptr_factory_(this) { + multidevice_setup_client_->AddObserver(this); + HandleMessagesFeatureState(); +} + +PairingLostNotifier::~PairingLostNotifier() { + multidevice_setup_client_->RemoveObserver(this); +} + +void PairingLostNotifier::OnFeatureStatesChanged( + const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap& + feature_states_map) { + HandleMessagesFeatureState(); +} + +void PairingLostNotifier::HandleMessagesFeatureState() { + multidevice_setup::mojom::FeatureState state = + multidevice_setup_client_->GetFeatureStates() + .find(multidevice_setup::mojom::Feature::kMessages) + ->second; + + // If Messages is currently enabled or disabled, the user has completed the + // setup process. + if (state == multidevice_setup::mojom::FeatureState::kDisabledByUser || + state == multidevice_setup::mojom::FeatureState::kEnabledByUser) { + HandleSetUpFeatureState(); + return; + } + + // If further setup is not required, there is no need to show a notification. + if (state != multidevice_setup::mojom::FeatureState::kFurtherSetupRequired) + return; + + // The Messages was not previously set up, the notification should not be + // shown. + if (!pref_service_->GetBoolean(kWasPreviouslySetUpPrefName)) + return; + + // Set the preference to false to indicate that the app was not previously set + // up, then show the notification. + pref_service_->SetBoolean(kWasPreviouslySetUpPrefName, false); + ShowPairingLostNotification(); +} + +void PairingLostNotifier::HandleSetUpFeatureState() { + // Store a preference indicating that the feature has been set up. This + // preference will be checked in the future in the case that the phone has + // become unpaired. + pref_service_->SetBoolean(kWasPreviouslySetUpPrefName, true); + + // If the "pairing lost" notification is currently visible, close it. + // Otherwise, the user could be confused that a notification is alerting the + // user to complete a task that has already been completed. + ClosePairingLostNotificationIfVisible(); +} + +void PairingLostNotifier::ShowPairingLostNotification() { + PA_LOG(INFO) << "PairingLostNotifier::ShowPairingLostNotification(): " + << "Pairing has been lost; displaying notification."; + + NotificationDisplayService::GetForProfile(profile_)->Display( + NotificationHandler::Type::TRANSIENT, + *ash::CreateSystemNotification( + message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, + kPairingLostNotificationId, + l10n_util::GetStringUTF16( + IDS_ANDROID_MESSAGES_PAIRING_LOST_NOTIFICATION_TITLE), + l10n_util::GetStringUTF16( + IDS_ANDROID_MESSAGES_PAIRING_LOST_NOTIFICATION_MESSAGE), + base::string16() /* display_source */, GURL() /* origin_url */, + message_center::NotifierId( + message_center::NotifierType::SYSTEM_COMPONENT, + kAndroidSmsNotifierId), + {} /* rich_notification_data */, + base::MakeRefCounted( + base::BindRepeating( + &PairingLostNotifier::OnPairingLostNotificationClick, + weak_ptr_factory_.GetWeakPtr())), + ash::kNotificationMessagesIcon, + message_center::SystemNotificationWarningLevel::NORMAL)); +} + +void PairingLostNotifier::ClosePairingLostNotificationIfVisible() { + PA_LOG(INFO) << "PairingLostNotifier::" + << "ClosePairingLostNotificationIfVisible(): " + << "Closing pairing lost notification if visible."; + + NotificationDisplayService::GetForProfile(profile_)->Close( + NotificationHandler::Type::TRANSIENT, kPairingLostNotificationId); +} + +void PairingLostNotifier::OnPairingLostNotificationClick( + base::Optional button_index) { + PA_LOG(INFO) << "PairingLostNotifier::OnPairingLostNotificationClick(): " + << "Pairing notification clicked; opening PWA."; + + ClosePairingLostNotificationIfVisible(); + android_sms_app_helper_delegate_->SetUpAndLaunchAndroidSmsApp(); +} + +} // namespace android_sms + +} // namespace chromeos diff --git a/chrome/browser/chromeos/android_sms/pairing_lost_notifier.h b/chrome/browser/chromeos/android_sms/pairing_lost_notifier.h new file mode 100644 index 0000000000000..3861ef01dc3ec --- /dev/null +++ b/chrome/browser/chromeos/android_sms/pairing_lost_notifier.h @@ -0,0 +1,66 @@ +// 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 CHROME_BROWSER_CHROMEOS_ANDROID_SMS_PAIRING_LOST_NOTIFIER_H_ +#define CHROME_BROWSER_CHROMEOS_ANDROID_SMS_PAIRING_LOST_NOTIFIER_H_ + +#include "base/memory/weak_ptr.h" +#include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client.h" + +class PrefRegistrySimple; +class PrefService; +class Profile; + +namespace chromeos { + +namespace multidevice_setup { +class AndroidSmsAppHelperDelegate; +} // namespace multidevice_setup + +namespace android_sms { + +// Displays a notification when a user loses pairing between their phone and +// Chromebook. +class PairingLostNotifier + : public multidevice_setup::MultiDeviceSetupClient::Observer { + public: + PairingLostNotifier( + Profile* profile, + multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client, + PrefService* pref_service, + multidevice_setup::AndroidSmsAppHelperDelegate* + android_sms_app_helper_delegate); + ~PairingLostNotifier() override; + + static void RegisterProfilePrefs(PrefRegistrySimple* registry); + + private: + // multidevice_setup::MultiDeviceSetupClient::Observer: + void OnFeatureStatesChanged( + const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap& + feature_states_map) override; + + void HandleMessagesFeatureState(); + void HandleSetUpFeatureState(); + + void ShowPairingLostNotification(); + void ClosePairingLostNotificationIfVisible(); + void OnPairingLostNotificationClick(base::Optional button_index); + + Profile* profile_; + multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_; + PrefService* pref_service_; + multidevice_setup::AndroidSmsAppHelperDelegate* + android_sms_app_helper_delegate_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PairingLostNotifier); +}; + +} // namespace android_sms + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_ANDROID_SMS_PAIRING_LOST_NOTIFIER_H_ diff --git a/chrome/browser/chromeos/android_sms/pairing_lost_notifier_unittest.cc b/chrome/browser/chromeos/android_sms/pairing_lost_notifier_unittest.cc new file mode 100644 index 0000000000000..16218bec83efc --- /dev/null +++ b/chrome/browser/chromeos/android_sms/pairing_lost_notifier_unittest.cc @@ -0,0 +1,145 @@ +// 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. + +#include "chrome/browser/chromeos/android_sms/pairing_lost_notifier.h" + +#include "chrome/browser/notifications/notification_display_service_tester.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h" +#include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace android_sms { + +namespace { + +const char kWasPreviouslySetUpPrefName[] = "android_sms.was_previously_set_up"; +const char kPairingLostNotificationId[] = "android_sms.pairing_lost"; + +} // namespace + +class PairingLostNotifierTest : public BrowserWithTestWindowTest { + protected: + PairingLostNotifierTest() = default; + ~PairingLostNotifierTest() override = default; + + void SetUp() override { + BrowserWithTestWindowTest::SetUp(); + + display_service_tester_ = + std::make_unique(profile()); + fake_multidevice_setup_client_ = + std::make_unique(); + test_pref_service_ = + std::make_unique(); + PairingLostNotifier::RegisterProfilePrefs(test_pref_service_->registry()); + fake_android_sms_app_helper_delegate_ = + std::make_unique(); + + pairing_lost_notifier_ = std::make_unique( + profile(), fake_multidevice_setup_client_.get(), + test_pref_service_.get(), fake_android_sms_app_helper_delegate_.get()); + } + + bool IsNotificationVisible() { + return display_service_tester_->GetNotification(kPairingLostNotificationId) + .has_value(); + } + + void ClickVisibleNotification() { + ASSERT_TRUE(IsNotificationVisible()); + display_service_tester_->SimulateClick( + NotificationHandler::Type::TRANSIENT, kPairingLostNotificationId, + base::nullopt /* action_index */, base::nullopt /* reply */); + } + + void SetWasPreviouslySetUpPreference(bool was_previously_set_up) { + test_pref_service_->SetBoolean(kWasPreviouslySetUpPrefName, + was_previously_set_up); + } + + bool GetWasPreviouslySetUpPreference() { + return test_pref_service_->GetBoolean(kWasPreviouslySetUpPrefName); + } + + bool WasAppLaunched() { + return fake_android_sms_app_helper_delegate_->has_launched_app(); + } + + void SetFeatureState(multidevice_setup::mojom::FeatureState feature_state) { + fake_multidevice_setup_client_->SetFeatureState( + multidevice_setup::mojom::Feature::kMessages, feature_state); + } + + private: + std::unique_ptr display_service_tester_; + std::unique_ptr + fake_multidevice_setup_client_; + std::unique_ptr + test_pref_service_; + std::unique_ptr + fake_android_sms_app_helper_delegate_; + + std::unique_ptr pairing_lost_notifier_; + + DISALLOW_COPY_AND_ASSIGN(PairingLostNotifierTest); +}; + +TEST_F(PairingLostNotifierTest, WasNotPreviouslySetUp) { + // Simulate the initial installation of the app. No notification should be + // displayed. + SetFeatureState( + multidevice_setup::mojom::FeatureState::kFurtherSetupRequired); + EXPECT_FALSE(IsNotificationVisible()); + + // Simulate initial setup. No notification should be displayed, but the + // preference should have been set to true. + SetFeatureState(multidevice_setup::mojom::FeatureState::kEnabledByUser); + EXPECT_FALSE(IsNotificationVisible()); + EXPECT_TRUE(GetWasPreviouslySetUpPreference()); +} + +TEST_F(PairingLostNotifierTest, WasPreviouslySetUp_ClickNotification) { + // Simulate the app having been previously set up. + SetWasPreviouslySetUpPreference(true /* was_previously_set_up */); + + // Transition back to the "setup required" state. This should trigger a + // notification, and the preference should have transitioned back to false to + // ensure that a duplicate notification is not shown. + SetFeatureState( + multidevice_setup::mojom::FeatureState::kFurtherSetupRequired); + EXPECT_TRUE(IsNotificationVisible()); + EXPECT_FALSE(GetWasPreviouslySetUpPreference()); + + // Clicking the notification should launch the PWA. + ClickVisibleNotification(); + EXPECT_TRUE(WasAppLaunched()); +} + +TEST_F(PairingLostNotifierTest, + WasPreviouslySetUp_CompleteSetupWithoutClickingNotification) { + // Simulate the app having been previously set up. + SetWasPreviouslySetUpPreference(true /* was_previously_set_up */); + + // Transition back to the "setup required" state. This should trigger a + // notification, and the preference should have transitioned back to false to + // ensure that a duplicate notification is not shown. + SetFeatureState( + multidevice_setup::mojom::FeatureState::kFurtherSetupRequired); + EXPECT_TRUE(IsNotificationVisible()); + EXPECT_FALSE(GetWasPreviouslySetUpPreference()); + + // Transition to the "enabled" state; this should cause the notification to be + // closed since it is no longer applicable. + SetFeatureState(multidevice_setup::mojom::FeatureState::kEnabledByUser); + EXPECT_FALSE(IsNotificationVisible()); + EXPECT_TRUE(GetWasPreviouslySetUpPreference()); +} + +} // namespace android_sms + +} // namespace chromeos