Commit 9f0ff082 authored by Ryan Hansberry's avatar Ryan Hansberry Committed by Chromium LUCI CQ

[Nearby] Suppress 'discoverable' Bluetooth notification during sharing.

If Nearby Share is actively broadcasting itself as able to receive
content (by making the device discoverable over Bluetooth), do not
display the "Your device is discoverable..." notification.

This change requires the addition of a new method,
NearbyShareDelegateImpl::IsEnableHighVisibilityRequestActive().
Using the existing method
NearbyShareDelegateImpl::IsHighVisibilityOn() is not sufficient
because BluetoothNotificationController::AdapterDiscoverableChanged()
is triggered before NearbyShareDelegateImpl::IsHighVisibilityOn()
becomes true.

Making generic Bluetooth UI logic (BluetoothNotificationController)
aware of the inner workings of Nearby is not ideal, but is the
quickest solution we have available before launch. We will clean
up this behavior leak in crbug.com/1155669.

Bug: 1155668
Change-Id: I3174d9e1af777b97ef6e5daed9a80f75831ca41f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2566407
Commit-Queue: Ryan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarJames Vecore <vecore@google.com>
Cr-Commit-Position: refs/heads/master@{#833897}
parent 3ac458d5
...@@ -27,6 +27,11 @@ class ASH_PUBLIC_EXPORT NearbyShareDelegate { ...@@ -27,6 +27,11 @@ class ASH_PUBLIC_EXPORT NearbyShareDelegate {
// Gets the current high visibility state from the NearbySharingService. // Gets the current high visibility state from the NearbySharingService.
virtual bool IsHighVisibilityOn() = 0; virtual bool IsHighVisibilityOn() = 0;
// Returns true if EnableHighVisibility() has been called but
// NearbyShareDelegate has not yet been informed that the request has
// concluded.
virtual bool IsEnableHighVisibilityRequestActive() const = 0;
// If high visibility is on, returns the time when the delegate // If high visibility is on, returns the time when the delegate
// will turn it off. May return any value if high visibility is off. // will turn it off. May return any value if high visibility is off.
virtual base::TimeTicks HighVisibilityShutoffTime() const = 0; virtual base::TimeTicks HighVisibilityShutoffTime() const = 0;
......
...@@ -20,6 +20,10 @@ bool TestNearbyShareDelegate::IsHighVisibilityOn() { ...@@ -20,6 +20,10 @@ bool TestNearbyShareDelegate::IsHighVisibilityOn() {
return is_high_visibility_on_; return is_high_visibility_on_;
} }
bool TestNearbyShareDelegate::IsEnableHighVisibilityRequestActive() const {
return is_enable_high_visibility_request_active_;
}
base::TimeTicks TestNearbyShareDelegate::HighVisibilityShutoffTime() const { base::TimeTicks TestNearbyShareDelegate::HighVisibilityShutoffTime() const {
return high_visibility_shutoff_time_; return high_visibility_shutoff_time_;
} }
......
...@@ -28,6 +28,7 @@ class ASH_PUBLIC_EXPORT TestNearbyShareDelegate : public NearbyShareDelegate { ...@@ -28,6 +28,7 @@ class ASH_PUBLIC_EXPORT TestNearbyShareDelegate : public NearbyShareDelegate {
// NearbyShareDelegate // NearbyShareDelegate
bool IsPodButtonVisible() override; bool IsPodButtonVisible() override;
bool IsHighVisibilityOn() override; bool IsHighVisibilityOn() override;
bool IsEnableHighVisibilityRequestActive() const override;
base::TimeTicks HighVisibilityShutoffTime() const override; base::TimeTicks HighVisibilityShutoffTime() const override;
void EnableHighVisibility() override; void EnableHighVisibility() override;
void DisableHighVisibility() override; void DisableHighVisibility() override;
...@@ -37,6 +38,12 @@ class ASH_PUBLIC_EXPORT TestNearbyShareDelegate : public NearbyShareDelegate { ...@@ -37,6 +38,12 @@ class ASH_PUBLIC_EXPORT TestNearbyShareDelegate : public NearbyShareDelegate {
is_pod_button_visible_ = visible; is_pod_button_visible_ = visible;
} }
void set_is_enable_high_visibility_request_active(
bool is_enable_high_visibility_request_active) {
is_enable_high_visibility_request_active_ =
is_enable_high_visibility_request_active;
}
void set_is_high_visibility_on(bool on) { is_high_visibility_on_ = on; } void set_is_high_visibility_on(bool on) { is_high_visibility_on_ = on; }
void set_high_visibility_shutoff_time(base::TimeTicks time) { void set_high_visibility_shutoff_time(base::TimeTicks time) {
...@@ -47,6 +54,7 @@ class ASH_PUBLIC_EXPORT TestNearbyShareDelegate : public NearbyShareDelegate { ...@@ -47,6 +54,7 @@ class ASH_PUBLIC_EXPORT TestNearbyShareDelegate : public NearbyShareDelegate {
private: private:
bool is_pod_button_visible_ = false; bool is_pod_button_visible_ = false;
bool is_enable_high_visibility_request_active_ = false;
bool is_high_visibility_on_ = false; bool is_high_visibility_on_ = false;
base::TimeTicks high_visibility_shutoff_time_; base::TimeTicks high_visibility_shutoff_time_;
std::vector<Method> method_calls_; std::vector<Method> method_calls_;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "ash/public/cpp/nearby_share_delegate.h"
#include "ash/public/cpp/notification_utils.h" #include "ash/public/cpp/notification_utils.h"
#include "ash/public/cpp/system_tray_client.h" #include "ash/public/cpp/system_tray_client.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
...@@ -296,8 +297,18 @@ void BluetoothNotificationController::OnGetAdapter( ...@@ -296,8 +297,18 @@ void BluetoothNotificationController::OnGetAdapter(
} }
void BluetoothNotificationController::NotifyAdapterDiscoverable() { void BluetoothNotificationController::NotifyAdapterDiscoverable() {
message_center::RichNotificationData optional; // If Nearby Share has made the local device discoverable, do not
// unnecessarily display this notification.
// TODO(crbug.com/1155669): Generalize this logic to prevent leaking Nearby
// implementation details.
auto* nearby_share_delegate = Shell::Get()->nearby_share_delegate();
if (nearby_share_delegate &&
(nearby_share_delegate->IsEnableHighVisibilityRequestActive() ||
nearby_share_delegate->IsHighVisibilityOn())) {
return;
}
message_center::RichNotificationData optional;
std::unique_ptr<Notification> notification = CreateSystemNotification( std::unique_ptr<Notification> notification = CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE, message_center::NOTIFICATION_TYPE_SIMPLE,
kBluetoothDeviceDiscoverableNotificationId, base::string16() /* title */, kBluetoothDeviceDiscoverableNotificationId, base::string16() /* title */,
......
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "ash/public/cpp/test/test_nearby_share_delegate.h"
#include "ash/public/cpp/test/test_system_tray_client.h" #include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/session/test_session_controller_client.h" #include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "ash/system/tray/tray_popup_utils.h" #include "ash/system/tray/tray_popup_utils.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
...@@ -16,17 +18,23 @@ ...@@ -16,17 +18,23 @@
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h" #include "device/bluetooth/test/mock_bluetooth_device.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/fake_message_center.h" #include "ui/message_center/fake_message_center.h"
using testing::NiceMock;
using testing::Return; using testing::Return;
namespace ash { namespace ash {
namespace { namespace {
const char kTestAdapterName[] = "Chromebook";
const char kTestAdapterAddress[] = "01:23:45:67:89:AB";
class TestMessageCenter : public message_center::FakeMessageCenter { class TestMessageCenter : public message_center::FakeMessageCenter {
public: public:
TestMessageCenter() = default; TestMessageCenter() = default;
...@@ -51,19 +59,29 @@ class BluetoothNotificationControllerTest : public AshTestBase { ...@@ -51,19 +59,29 @@ class BluetoothNotificationControllerTest : public AshTestBase {
void SetUp() override { void SetUp() override {
AshTestBase::SetUp(); AshTestBase::SetUp();
mock_adapter_ =
base::MakeRefCounted<NiceMock<device::MockBluetoothAdapter>>();
ON_CALL(*mock_adapter_, IsPresent()).WillByDefault(Return(true));
ON_CALL(*mock_adapter_, IsPowered()).WillByDefault(Return(true));
ON_CALL(*mock_adapter_, GetName()).WillByDefault(Return(kTestAdapterName));
ON_CALL(*mock_adapter_, GetAddress())
.WillByDefault(Return(kTestAdapterAddress));
device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
notification_controller_ = notification_controller_ =
std::make_unique<BluetoothNotificationController>( std::make_unique<BluetoothNotificationController>(
&test_message_center_); &test_message_center_);
system_tray_client_ = GetSystemTrayClient(); system_tray_client_ = GetSystemTrayClient();
bluetooth_device_1_ = bluetooth_device_1_ =
std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>( std::make_unique<NiceMock<device::MockBluetoothDevice>>(
nullptr /* adapter */, 0 /* bluetooth_class */, "name_1", mock_adapter_.get(), 0 /* bluetooth_class */, "name_1", "address_1",
"address_1", false /* paired */, false /* connected */); false /* paired */, false /* connected */);
bluetooth_device_2_ = bluetooth_device_2_ =
std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>( std::make_unique<NiceMock<device::MockBluetoothDevice>>(
nullptr /* adapter */, 0 /* bluetooth_class */, "name_2", mock_adapter_.get(), 0 /* bluetooth_class */, "name_2", "address_2",
"address_2", false /* paired */, false /* connected */); false /* paired */, false /* connected */);
} }
void ClickPairedNotification(const device::BluetoothDevice* device) { void ClickPairedNotification(const device::BluetoothDevice* device) {
...@@ -78,6 +96,26 @@ class BluetoothNotificationControllerTest : public AshTestBase { ...@@ -78,6 +96,26 @@ class BluetoothNotificationControllerTest : public AshTestBase {
by_user); by_user);
} }
void VerifyDiscoverableNotificationIsNotVisible() {
EXPECT_FALSE(test_message_center_.FindVisibleNotificationById(
BluetoothNotificationController::
kBluetoothDeviceDiscoverableNotificationId));
}
void VerifyDiscoverableNotificationIsVisible() {
message_center::Notification* visible_notification =
test_message_center_.FindVisibleNotificationById(
BluetoothNotificationController::
kBluetoothDeviceDiscoverableNotificationId);
EXPECT_TRUE(visible_notification);
EXPECT_EQ(base::string16(), visible_notification->title());
EXPECT_EQ(
l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE,
base::UTF8ToUTF16(kTestAdapterName),
base::UTF8ToUTF16(kTestAdapterAddress)),
visible_notification->message());
}
void VerifyPairedNotificationIsNotVisible( void VerifyPairedNotificationIsNotVisible(
const device::BluetoothDevice* device) { const device::BluetoothDevice* device) {
EXPECT_FALSE(test_message_center_.FindVisibleNotificationById( EXPECT_FALSE(test_message_center_.FindVisibleNotificationById(
...@@ -98,6 +136,11 @@ class BluetoothNotificationControllerTest : public AshTestBase { ...@@ -98,6 +136,11 @@ class BluetoothNotificationControllerTest : public AshTestBase {
// Run the notification controller to simulate showing a notification by // Run the notification controller to simulate showing a notification by
// adding it to the TestMessageCenter. // adding it to the TestMessageCenter.
void ShowDiscoverableNotification(
BluetoothNotificationController* notification_controller) {
notification_controller->NotifyAdapterDiscoverable();
}
void ShowPairedNotification( void ShowPairedNotification(
BluetoothNotificationController* notification_controller, BluetoothNotificationController* notification_controller,
device::MockBluetoothDevice* bluetooth_device) { device::MockBluetoothDevice* bluetooth_device) {
...@@ -105,6 +148,7 @@ class BluetoothNotificationControllerTest : public AshTestBase { ...@@ -105,6 +148,7 @@ class BluetoothNotificationControllerTest : public AshTestBase {
} }
TestMessageCenter test_message_center_; TestMessageCenter test_message_center_;
scoped_refptr<device::MockBluetoothAdapter> mock_adapter_;
std::unique_ptr<BluetoothNotificationController> notification_controller_; std::unique_ptr<BluetoothNotificationController> notification_controller_;
TestSystemTrayClient* system_tray_client_; TestSystemTrayClient* system_tray_client_;
std::unique_ptr<device::MockBluetoothDevice> bluetooth_device_1_; std::unique_ptr<device::MockBluetoothDevice> bluetooth_device_1_;
...@@ -113,6 +157,40 @@ class BluetoothNotificationControllerTest : public AshTestBase { ...@@ -113,6 +157,40 @@ class BluetoothNotificationControllerTest : public AshTestBase {
DISALLOW_COPY_AND_ASSIGN(BluetoothNotificationControllerTest); DISALLOW_COPY_AND_ASSIGN(BluetoothNotificationControllerTest);
}; };
TEST_F(BluetoothNotificationControllerTest, DiscoverableNotification) {
VerifyDiscoverableNotificationIsNotVisible();
ShowDiscoverableNotification(notification_controller_.get());
VerifyDiscoverableNotificationIsVisible();
}
TEST_F(BluetoothNotificationControllerTest,
DiscoverableNotification_NearbyShareEnableHighVisibilityRequestActive) {
VerifyDiscoverableNotificationIsNotVisible();
auto* nearby_share_delegate_ = static_cast<TestNearbyShareDelegate*>(
Shell::Get()->nearby_share_delegate());
nearby_share_delegate_->set_is_enable_high_visibility_request_active(true);
ShowDiscoverableNotification(notification_controller_.get());
VerifyDiscoverableNotificationIsNotVisible();
}
TEST_F(BluetoothNotificationControllerTest,
DiscoverableNotification_NearbyShareHighVisibilityOn) {
VerifyDiscoverableNotificationIsNotVisible();
auto* nearby_share_delegate_ = static_cast<TestNearbyShareDelegate*>(
Shell::Get()->nearby_share_delegate());
nearby_share_delegate_->set_is_high_visibility_on(true);
ShowDiscoverableNotification(notification_controller_.get());
VerifyDiscoverableNotificationIsNotVisible();
}
TEST_F(BluetoothNotificationControllerTest, TEST_F(BluetoothNotificationControllerTest,
PairedDeviceNotification_TapNotification) { PairedDeviceNotification_TapNotification) {
// Show the notification to the user. // Show the notification to the user.
......
...@@ -59,6 +59,10 @@ bool NearbyShareDelegateImpl::IsHighVisibilityOn() { ...@@ -59,6 +59,10 @@ bool NearbyShareDelegateImpl::IsHighVisibilityOn() {
return nearby_share_service_ && nearby_share_service_->IsInHighVisibility(); return nearby_share_service_ && nearby_share_service_->IsInHighVisibility();
} }
bool NearbyShareDelegateImpl::IsEnableHighVisibilityRequestActive() const {
return is_enable_high_visibility_request_active_;
}
base::TimeTicks NearbyShareDelegateImpl::HighVisibilityShutoffTime() const { base::TimeTicks NearbyShareDelegateImpl::HighVisibilityShutoffTime() const {
return shutoff_time_; return shutoff_time_;
} }
...@@ -72,6 +76,8 @@ void NearbyShareDelegateImpl::EnableHighVisibility() { ...@@ -72,6 +76,8 @@ void NearbyShareDelegateImpl::EnableHighVisibility() {
if (!nearby_share_service_->GetSettings()->GetEnabled()) { if (!nearby_share_service_->GetSettings()->GetEnabled()) {
onboarding_wait_timer_.Reset(); onboarding_wait_timer_.Reset();
} }
is_enable_high_visibility_request_active_ = true;
} }
void NearbyShareDelegateImpl::DisableHighVisibility() { void NearbyShareDelegateImpl::DisableHighVisibility() {
...@@ -127,6 +133,8 @@ void NearbyShareDelegateImpl::OnEnabledChanged(bool enabled) { ...@@ -127,6 +133,8 @@ void NearbyShareDelegateImpl::OnEnabledChanged(bool enabled) {
} }
void NearbyShareDelegateImpl::OnHighVisibilityChanged(bool high_visibility_on) { void NearbyShareDelegateImpl::OnHighVisibilityChanged(bool high_visibility_on) {
is_enable_high_visibility_request_active_ = false;
if (high_visibility_on) { if (high_visibility_on) {
shutoff_time_ = base::TimeTicks::Now() + kShutoffTimeout; shutoff_time_ = base::TimeTicks::Now() + kShutoffTimeout;
shutoff_timer_.Reset(); shutoff_timer_.Reset();
......
...@@ -56,6 +56,7 @@ class NearbyShareDelegateImpl ...@@ -56,6 +56,7 @@ class NearbyShareDelegateImpl
// ash::NearbyShareDelegate // ash::NearbyShareDelegate
bool IsPodButtonVisible() override; bool IsPodButtonVisible() override;
bool IsHighVisibilityOn() override; bool IsHighVisibilityOn() override;
bool IsEnableHighVisibilityRequestActive() const override;
base::TimeTicks HighVisibilityShutoffTime() const override; base::TimeTicks HighVisibilityShutoffTime() const override;
void EnableHighVisibility() override; void EnableHighVisibility() override;
void DisableHighVisibility() override; void DisableHighVisibility() override;
...@@ -92,6 +93,10 @@ class NearbyShareDelegateImpl ...@@ -92,6 +93,10 @@ class NearbyShareDelegateImpl
NearbySharingService* nearby_share_service_ = nullptr; NearbySharingService* nearby_share_service_ = nullptr;
std::unique_ptr<SettingsOpener> settings_opener_; std::unique_ptr<SettingsOpener> settings_opener_;
// Track if there is an outstanding request to enable high visibility. Reset
// to false once the request finishes (via OnHighVisibilityChanged());
bool is_enable_high_visibility_request_active_ = false;
// This timer is used to automatically turn off high visibility after a // This timer is used to automatically turn off high visibility after a
// timeout. // timeout.
base::RetainingOneShotTimer shutoff_timer_; base::RetainingOneShotTimer shutoff_timer_;
......
...@@ -139,6 +139,18 @@ TEST_F(NearbyShareDelegateImplTest, StartStopHighVisibility) { ...@@ -139,6 +139,18 @@ TEST_F(NearbyShareDelegateImplTest, StartStopHighVisibility) {
SetHighVisibilityOn(false); SetHighVisibilityOn(false);
} }
TEST_F(NearbyShareDelegateImplTest, TestIsEnableHighVisibilityRequestActive) {
settings()->SetEnabled(true);
EXPECT_CALL(*settings_opener_, ShowSettingsPage(_));
EXPECT_CALL(controller_, HighVisibilityEnabledChanged(true));
delegate_.EnableHighVisibility();
EXPECT_TRUE(delegate_.IsEnableHighVisibilityRequestActive());
SetHighVisibilityOn(true);
EXPECT_FALSE(delegate_.IsEnableHighVisibilityRequestActive());
}
TEST_F(NearbyShareDelegateImplTest, ShowOnboardingAndTurnOnHighVisibility) { TEST_F(NearbyShareDelegateImplTest, ShowOnboardingAndTurnOnHighVisibility) {
settings()->SetEnabled(false); settings()->SetEnabled(false);
......
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