Commit aa2d2f02 authored by Johann Koenig's avatar Johann Koenig Committed by Commit Bot

Revert "[CrOS PhoneHub] Add PhoneHubUiController."

This reverts commit ac56b3e7.

Reason for revert: PhoneHubUiControllerTest.NotEligibleForFeature
fails on msan builds:
$ autoninja -C out/chromeos_msan ash_unittests && ./out/chromeos_msan/ash_unittests --gtest_filter="*PhoneHubUiControllerTest*"

[==========] Running 9 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 9 tests from PhoneHubUiControllerTest
[ RUN      ] PhoneHubUiControllerTest.NotEligibleForFeature
==1672876==WARNING: MemorySanitizer: use-of-uninitialized-value

https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Tests/20740
PhoneHubUiControllerTest.BluetoothOff
PhoneHubUiControllerTest.NotEligibleForFeature
PhoneHubUiControllerTest.OnboardingNotEligible
PhoneHubUiControllerTest.PhoneConnected
PhoneHubUiControllerTest.PhoneConnecting
PhoneHubUiControllerTest.PhoneConnectingForOnboarding
PhoneHubUiControllerTest.PhoneDisconnected
PhoneHubUiControllerTest.ShowOnboardingUi_WithPhone
PhoneHubUiControllerTest.ShowOnboardingUi_WithoutPhone

Original change's description:
> [CrOS PhoneHub] Add PhoneHubUiController.
>
> This class translates the PhoneHubManager state into the corresponding
> UI state and view that is shown in the tray bubble. Each state has
> it's own view class and the PhoneHubTray bubble transitions through
> these content views as the UI state changes.
>
> This CL also refactors PhoneConnectedView into it's own class.
>
> BUG=1106937,1126208
>
> Change-Id: Ie012077a1905f80be91fbfb0b084d89219bd6c26
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2427060
> Commit-Queue: Tim Song <tengs@chromium.org>
> Reviewed-by: Kyle Horimoto <khorimoto@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#810942}

TBR=khorimoto@chromium.org,tengs@chromium.org,leandre@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: 1106937
Bug: 1126208
Change-Id: Ibd11174488010a7b210896d8f16943cfba90f918
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2434010Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarJohann Koenig <johannkoenig@google.com>
Commit-Queue: Johann Koenig <johannkoenig@google.com>
Cr-Commit-Position: refs/heads/master@{#811100}
parent 13f14948
...@@ -1124,16 +1124,12 @@ component("ash") { ...@@ -1124,16 +1124,12 @@ component("ash") {
"system/phonehub/notification_opt_in_view.h", "system/phonehub/notification_opt_in_view.h",
"system/phonehub/onboarding_view.cc", "system/phonehub/onboarding_view.cc",
"system/phonehub/onboarding_view.h", "system/phonehub/onboarding_view.h",
"system/phonehub/phone_connected_view.cc",
"system/phonehub/phone_connected_view.h",
"system/phonehub/phone_hub_interstitial_view.cc", "system/phonehub/phone_hub_interstitial_view.cc",
"system/phonehub/phone_hub_interstitial_view.h", "system/phonehub/phone_hub_interstitial_view.h",
"system/phonehub/phone_hub_notification_controller.cc", "system/phonehub/phone_hub_notification_controller.cc",
"system/phonehub/phone_hub_notification_controller.h", "system/phonehub/phone_hub_notification_controller.h",
"system/phonehub/phone_hub_tray.cc", "system/phonehub/phone_hub_tray.cc",
"system/phonehub/phone_hub_tray.h", "system/phonehub/phone_hub_tray.h",
"system/phonehub/phone_hub_ui_controller.cc",
"system/phonehub/phone_hub_ui_controller.h",
"system/phonehub/phone_hub_view_ids.h", "system/phonehub/phone_hub_view_ids.h",
"system/phonehub/phone_status_view.cc", "system/phonehub/phone_status_view.cc",
"system/phonehub/phone_status_view.h", "system/phonehub/phone_status_view.h",
...@@ -2121,7 +2117,6 @@ test("ash_unittests") { ...@@ -2121,7 +2117,6 @@ test("ash_unittests") {
"system/palette/tools/screenshot_unittest.cc", "system/palette/tools/screenshot_unittest.cc",
"system/phonehub/phone_hub_notification_controller_unittest.cc", "system/phonehub/phone_hub_notification_controller_unittest.cc",
"system/phonehub/phone_hub_tray_unittest.cc", "system/phonehub/phone_hub_tray_unittest.cc",
"system/phonehub/phone_hub_ui_controller_unittest.cc",
"system/phonehub/phone_status_view_unittest.cc", "system/phonehub/phone_status_view_unittest.cc",
"system/phonehub/task_continuation_view_unittest.cc", "system/phonehub/task_continuation_view_unittest.cc",
"system/power/backlights_forced_off_setter_unittest.cc", "system/power/backlights_forced_off_setter_unittest.cc",
......
...@@ -12,6 +12,12 @@ namespace ash { ...@@ -12,6 +12,12 @@ namespace ash {
class PhoneHubInterstitialView; class PhoneHubInterstitialView;
// Defines possible connection error states of the Phone Hub feature.
enum class ErrorStatus {
kDisconnected, // The connection to the phone has been interrupted.
kReconnecting, // Attempts to resume the connection to the phone.
};
// An interstitial view represeting that the Phone Hub feature is not available // An interstitial view represeting that the Phone Hub feature is not available
// due to connection issues. // due to connection issues.
class ASH_EXPORT ConnectionErrorView : public views::View, class ASH_EXPORT ConnectionErrorView : public views::View,
...@@ -19,12 +25,6 @@ class ASH_EXPORT ConnectionErrorView : public views::View, ...@@ -19,12 +25,6 @@ class ASH_EXPORT ConnectionErrorView : public views::View,
public: public:
METADATA_HEADER(ConnectionErrorView); METADATA_HEADER(ConnectionErrorView);
// Defines possible connection error states of the Phone Hub feature.
enum class ErrorStatus {
kDisconnected, // The connection to the phone has been interrupted.
kReconnecting, // Attempts to resume the connection to the phone.
};
explicit ConnectionErrorView(ErrorStatus error); explicit ConnectionErrorView(ErrorStatus error);
ConnectionErrorView(const ConnectionErrorView&) = delete; ConnectionErrorView(const ConnectionErrorView&) = delete;
ConnectionErrorView& operator=(const ConnectionErrorView&) = delete; ConnectionErrorView& operator=(const ConnectionErrorView&) = delete;
......
// 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 "ash/system/phonehub/phone_connected_view.h"
#include <memory>
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/notification_opt_in_view.h"
#include "ash/system/phonehub/phone_status_view.h"
#include "ash/system/phonehub/quick_actions_view.h"
#include "ash/system/phonehub/task_continuation_view.h"
#include "ash/system/tray/tray_constants.h"
#include "chromeos/components/phonehub/notification_access_manager.h"
#include "chromeos/components/phonehub/phone_hub_manager.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
constexpr int kPaddingBetweenTitleAndSeparator = 3;
} // namespace
PhoneConnectedView::PhoneConnectedView(
TrayBubbleView* bubble_view,
chromeos::phonehub::PhoneHubManager* phone_hub_manager) {
auto setup_layered_view = [](views::View* view) {
view->SetPaintToLayer();
view->layer()->SetFillsBoundsOpaquely(false);
};
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(0, 0, 0, 0)));
layout->SetDefaultFlex(1);
AddSeparator();
// TODO(meilinw): handle the case when the user has dismissed this opt in
// view once, we shouldn't show it again.
if (!phone_hub_manager->GetNotificationAccessManager()
->HasAccessBeenGranted()) {
AddChildView(std::make_unique<NotificationOptInView>(bubble_view));
}
setup_layered_view(AddChildView(std::make_unique<QuickActionsView>()));
AddSeparator();
auto* phone_model = phone_hub_manager->GetPhoneModel();
if (phone_model) {
setup_layered_view(
AddChildView(std::make_unique<TaskContinuationView>(phone_model)));
}
}
PhoneConnectedView::~PhoneConnectedView() = default;
const char* PhoneConnectedView::GetClassName() const {
return "PhoneConnectedView";
}
void PhoneConnectedView::AddSeparator() {
auto* separator = AddChildView(std::make_unique<views::Separator>());
separator->SetPaintToLayer();
separator->layer()->SetFillsBoundsOpaquely(false);
separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor));
separator->SetBorder(views::CreateEmptyBorder(gfx::Insets(
kPaddingBetweenTitleAndSeparator, 0, kMenuSeparatorVerticalPadding, 0)));
}
} // namespace ash
// 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 ASH_SYSTEM_PHONEHUB_PHONE_CONNECTED_VIEW_H_
#define ASH_SYSTEM_PHONEHUB_PHONE_CONNECTED_VIEW_H_
#include "ash/ash_export.h"
#include "ui/views/view.h"
namespace chromeos {
namespace phonehub {
class PhoneHubManager;
} // namespace phonehub
} // namespace chromeos
namespace ash {
class TrayBubbleView;
// A view of the Phone Hub panel, displaying phone status and utility actions
// such as phone status, task continuation, etc.
class PhoneConnectedView : public views::View {
public:
PhoneConnectedView(TrayBubbleView* bubble_view,
chromeos::phonehub::PhoneHubManager* phone_hub_manager);
~PhoneConnectedView() override;
// views::View:
const char* GetClassName() const override;
private:
void AddSeparator();
};
} // namespace ash
#endif // ASH_SYSTEM_PHONEHUB_PHONE_CONNECTED_VIEW_H_
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
#include "ash/system/phonehub/phone_hub_tray.h" #include "ash/system/phonehub/phone_hub_tray.h"
#include <memory>
#include "ash/accessibility/accessibility_controller_impl.h" #include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shelf/shelf.h" #include "ash/shelf/shelf.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h" #include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/notification_opt_in_view.h"
#include "ash/system/phonehub/phone_status_view.h" #include "ash/system/phonehub/phone_status_view.h"
#include "ash/system/phonehub/quick_actions_view.h" #include "ash/system/phonehub/quick_actions_view.h"
#include "ash/system/phonehub/task_continuation_view.h" #include "ash/system/phonehub/task_continuation_view.h"
...@@ -21,6 +24,7 @@ ...@@ -21,6 +24,7 @@
#include "ash/system/tray/tray_popup_utils.h" #include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/tray/tray_utils.h" #include "ash/system/tray/tray_utils.h"
#include "base/bind.h" #include "base/bind.h"
#include "chromeos/components/phonehub/notification_access_manager.h"
#include "chromeos/components/phonehub/phone_hub_manager.h" #include "chromeos/components/phonehub/phone_hub_manager.h"
#include "chromeos/components/phonehub/phone_model.h" #include "chromeos/components/phonehub/phone_model.h"
#include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_features.h"
...@@ -29,6 +33,8 @@ ...@@ -29,6 +33,8 @@
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/separator.h"
namespace ash { namespace ash {
...@@ -40,13 +46,67 @@ constexpr int kTrayIconCrossAxisInset = 0; ...@@ -40,13 +46,67 @@ constexpr int kTrayIconCrossAxisInset = 0;
constexpr gfx::Insets kBubblePadding(4, 16); constexpr gfx::Insets kBubblePadding(4, 16);
constexpr int kBubbleWidth = 400; constexpr int kBubbleWidth = 400;
constexpr int kPaddingBetweenTitleAndSeparator = 3;
// A content view of the Phone Hub panel, displaying utility actions
// such as quick actions, task continuation, etc.
class PhoneHubView : public views ::View {
public:
explicit PhoneHubView(TrayBubbleView* bubble_view,
chromeos::phonehub::PhoneHubManager* phone_hub_manager)
: bubble_view_(bubble_view) {
auto setup_layered_view = [](views::View* view) {
view->SetPaintToLayer();
view->layer()->SetFillsBoundsOpaquely(false);
};
AddSeparator();
// TODO(meilinw): handle the case when the user has dismissed this opt in
// view once, we shouldn't show it again.
if (!phone_hub_manager->GetNotificationAccessManager()
->HasAccessBeenGranted()) {
bubble_view_->AddChildView(
std::make_unique<NotificationOptInView>(bubble_view_));
}
setup_layered_view(
bubble_view_->AddChildView(std::make_unique<QuickActionsView>()));
AddSeparator();
chromeos::phonehub::PhoneModel* phone_model =
phone_hub_manager->GetPhoneModel();
if (phone_model) {
setup_layered_view(bubble_view->AddChildView(
std::make_unique<TaskContinuationView>(phone_model)));
}
}
~PhoneHubView() override = default;
// views::View:
const char* GetClassName() const override { return "PhoneHubView"; }
private:
void AddSeparator() {
auto* separator =
bubble_view_->AddChildView(std::make_unique<views::Separator>());
separator->SetPaintToLayer();
separator->layer()->SetFillsBoundsOpaquely(false);
separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor));
separator->SetBorder(views::CreateEmptyBorder(
gfx::Insets(kPaddingBetweenTitleAndSeparator, 0,
kMenuSeparatorVerticalPadding, 0)));
}
} // namespace TrayBubbleView* bubble_view_ = nullptr;
};
PhoneHubTray::PhoneHubTray(Shelf* shelf) } // namespace
: TrayBackgroundView(shelf), ui_controller_(new PhoneHubUiController()) {
observed_phone_hub_ui_controller_.Add(ui_controller_.get());
PhoneHubTray::PhoneHubTray(Shelf* shelf) : TrayBackgroundView(shelf) {
// TODO(tengs): Update icon to spec. // TODO(tengs): Update icon to spec.
auto icon = std::make_unique<views::ImageView>(); auto icon = std::make_unique<views::ImageView>();
icon->SetTooltipText( icon->SetTooltipText(
...@@ -63,11 +123,21 @@ PhoneHubTray::PhoneHubTray(Shelf* shelf) ...@@ -63,11 +123,21 @@ PhoneHubTray::PhoneHubTray(Shelf* shelf)
PhoneHubTray::~PhoneHubTray() { PhoneHubTray::~PhoneHubTray() {
if (bubble_) if (bubble_)
bubble_->bubble_view()->ResetDelegate(); bubble_->bubble_view()->ResetDelegate();
CleanUpPhoneHubManager();
} }
void PhoneHubTray::SetPhoneHubManager( void PhoneHubTray::SetPhoneHubManager(
chromeos::phonehub::PhoneHubManager* phone_hub_manager) { chromeos::phonehub::PhoneHubManager* phone_hub_manager) {
ui_controller_->SetPhoneHubManager(phone_hub_manager); if (phone_hub_manager == phone_hub_manager_)
return;
CleanUpPhoneHubManager();
phone_hub_manager_ = phone_hub_manager;
if (phone_hub_manager_)
phone_hub_manager_->GetFeatureStatusProvider()->AddObserver(this);
OnFeatureStatusChanged();
} }
void PhoneHubTray::ClickedOutsideBubble() { void PhoneHubTray::ClickedOutsideBubble() {
...@@ -100,27 +170,6 @@ void PhoneHubTray::HideBubble(const TrayBubbleView* bubble_view) { ...@@ -100,27 +170,6 @@ void PhoneHubTray::HideBubble(const TrayBubbleView* bubble_view) {
HideBubbleWithView(bubble_view); HideBubbleWithView(bubble_view);
} }
void PhoneHubTray::OnPhoneHubUiStateChanged() {
UpdateVisibility();
if (!bubble_)
return;
TrayBubbleView* bubble_view = bubble_->bubble_view();
DCHECK(ui_controller_.get());
std::unique_ptr<views::View> content_view =
ui_controller_->CreateContentView(bubble_view);
if (!content_view.get()) {
CloseBubble();
return;
}
if (content_view_)
bubble_view->RemoveChildView(content_view_);
content_view_ = content_view.get();
bubble_view->AddChildView(std::move(content_view));
}
void PhoneHubTray::AnchorUpdated() { void PhoneHubTray::AnchorUpdated() {
if (bubble_) if (bubble_)
bubble_->bubble_view()->UpdateBubble(); bubble_->bubble_view()->UpdateBubble();
...@@ -145,6 +194,8 @@ void PhoneHubTray::ShowBubble(bool show_by_click) { ...@@ -145,6 +194,8 @@ void PhoneHubTray::ShowBubble(bool show_by_click) {
if (bubble_) if (bubble_)
return; return;
DCHECK(phone_hub_manager_);
TrayBubbleView::InitParams init_params; TrayBubbleView::InitParams init_params;
init_params.delegate = this; init_params.delegate = this;
init_params.parent_window = GetBubbleWindowContainer(); init_params.parent_window = GetBubbleWindowContainer();
...@@ -164,25 +215,23 @@ void PhoneHubTray::ShowBubble(bool show_by_click) { ...@@ -164,25 +215,23 @@ void PhoneHubTray::ShowBubble(bool show_by_click) {
// We will always have this phone status view on top of the bubble view // We will always have this phone status view on top of the bubble view
// to display any available phone status and the settings icon. // to display any available phone status and the settings icon.
std::unique_ptr<views::View> phone_status = chromeos::phonehub::PhoneModel* phone_model =
ui_controller_->CreateStatusHeaderView(); phone_hub_manager_->GetPhoneModel();
if (phone_status) { if (phone_model) {
auto* phone_status = bubble_view->AddChildView(
std::make_unique<PhoneStatusView>(phone_model));
phone_status->SetPaintToLayer(); phone_status->SetPaintToLayer();
phone_status->layer()->SetFillsBoundsOpaquely(false); phone_status->layer()->SetFillsBoundsOpaquely(false);
bubble_view->AddChildView(std::move(phone_status));
} }
// Other contents, i.e. the connected view and the interstitial views, // Other contents, i.e. the connected view and the interstitial views,
// will be positioned underneath the phone status view and updated based // will be positioned underneath the phone status view and updated based
// on the current mode. // on the current mode.
auto content_view = ui_controller_->CreateContentView(bubble_view); bubble_view->AddChildView(
content_view_ = content_view.get(); std::make_unique<PhoneHubView>(bubble_view, phone_hub_manager_));
if (content_view_)
bubble_view->AddChildView(std::move(content_view));
bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view, bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view,
false /* is_persistent */); false /* is_persistent */);
SetIsActive(true); SetIsActive(true);
} }
...@@ -195,16 +244,43 @@ const char* PhoneHubTray::GetClassName() const { ...@@ -195,16 +244,43 @@ const char* PhoneHubTray::GetClassName() const {
} }
void PhoneHubTray::CloseBubble() { void PhoneHubTray::CloseBubble() {
content_view_ = nullptr;
bubble_.reset(); bubble_.reset();
SetIsActive(false); SetIsActive(false);
shelf()->UpdateAutoHideState(); shelf()->UpdateAutoHideState();
} }
void PhoneHubTray::OnFeatureStatusChanged() {
UpdateVisibility();
}
void PhoneHubTray::UpdateVisibility() { void PhoneHubTray::UpdateVisibility() {
DCHECK(ui_controller_.get()); if (!phone_hub_manager_) {
auto ui_state = ui_controller_->ui_state(); SetVisiblePreferred(false);
SetVisiblePreferred(ui_state != PhoneHubUiController::UiState::kHidden); return;
}
auto feature_status =
phone_hub_manager_->GetFeatureStatusProvider()->GetStatus();
bool is_visible;
switch (feature_status) {
case chromeos::phonehub::FeatureStatus::kNotEligibleForFeature:
FALLTHROUGH;
case chromeos::phonehub::FeatureStatus::kDisabled:
is_visible = false;
break;
default:
is_visible = true;
break;
}
SetVisiblePreferred(is_visible);
}
void PhoneHubTray::CleanUpPhoneHubManager() {
if (!phone_hub_manager_)
return;
phone_hub_manager_->GetFeatureStatusProvider()->RemoveObserver(this);
} }
} // namespace ash } // namespace ash
...@@ -6,9 +6,8 @@ ...@@ -6,9 +6,8 @@
#define ASH_SYSTEM_PHONEHUB_PHONE_HUB_TRAY_H_ #define ASH_SYSTEM_PHONEHUB_PHONE_HUB_TRAY_H_
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/system/phonehub/phone_hub_ui_controller.h"
#include "ash/system/tray/tray_background_view.h" #include "ash/system/tray/tray_background_view.h"
#include "base/scoped_observer.h" #include "chromeos/components/phonehub/feature_status_provider.h"
namespace chromeos { namespace chromeos {
namespace phonehub { namespace phonehub {
...@@ -26,8 +25,9 @@ class TrayBubbleWrapper; ...@@ -26,8 +25,9 @@ class TrayBubbleWrapper;
// This class represents the Phone Hub tray button in the status area and // This class represents the Phone Hub tray button in the status area and
// controls the bubble that is shown when the tray button is clicked. // controls the bubble that is shown when the tray button is clicked.
class ASH_EXPORT PhoneHubTray : public TrayBackgroundView, class ASH_EXPORT PhoneHubTray
public PhoneHubUiController::Observer { : public TrayBackgroundView,
public chromeos::phonehub::FeatureStatusProvider::Observer {
public: public:
explicit PhoneHubTray(Shelf* shelf); explicit PhoneHubTray(Shelf* shelf);
PhoneHubTray(const PhoneHubTray&) = delete; PhoneHubTray(const PhoneHubTray&) = delete;
...@@ -51,37 +51,29 @@ class ASH_EXPORT PhoneHubTray : public TrayBackgroundView, ...@@ -51,37 +51,29 @@ class ASH_EXPORT PhoneHubTray : public TrayBackgroundView,
TrayBubbleView* GetBubbleView() override; TrayBubbleView* GetBubbleView() override;
const char* GetClassName() const override; const char* GetClassName() const override;
views::View* content_view_for_testing() { return content_view_; }
private: private:
// TrayBubbleView::Delegate: // TrayBubbleView::Delegate:
base::string16 GetAccessibleNameForBubble() override; base::string16 GetAccessibleNameForBubble() override;
bool ShouldEnableExtraKeyboardAccessibility() override; bool ShouldEnableExtraKeyboardAccessibility() override;
void HideBubble(const TrayBubbleView* bubble_view) override; void HideBubble(const TrayBubbleView* bubble_view) override;
// PhoneHubUiController::Observer: // chromeos::phonehub::FeatureStatusProvider::Observer:
void OnPhoneHubUiStateChanged() override; void OnFeatureStatusChanged() override;
// Updates the visibility of the tray in the shelf based on the feature is // Updates the visibility of the tray in the shelf based on the feature is
// enabled. // enabled.
void UpdateVisibility(); void UpdateVisibility();
// Cleans up |phone_hub_manager_| by removing all observers.
void CleanUpPhoneHubManager();
// Icon of the tray. Unowned. // Icon of the tray. Unowned.
views::ImageView* icon_; views::ImageView* icon_;
// Controls the main content view displayed in the bubble based on the current // The PhoneHubManager that provides data for the UI.
// PhoneHub state. chromeos::phonehub::PhoneHubManager* phone_hub_manager_ = nullptr;
std::unique_ptr<PhoneHubUiController> ui_controller_;
// The bubble that appears after clicking the tray button.
std::unique_ptr<TrayBubbleWrapper> bubble_; std::unique_ptr<TrayBubbleWrapper> bubble_;
// The main content view of the bubble, which changes depending on the state.
// Unowned.
views::View* content_view_;
ScopedObserver<PhoneHubUiController, PhoneHubUiController::Observer>
observed_phone_hub_ui_controller_{this};
}; };
} // namespace ash } // namespace ash
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include "chromeos/components/phonehub/fake_phone_hub_manager.h" #include "chromeos/components/phonehub/fake_phone_hub_manager.h"
#include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_features.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "ui/events/event.h"
namespace ash { namespace ash {
...@@ -87,26 +86,26 @@ class PhoneHubTrayTest : public AshTestBase { ...@@ -87,26 +86,26 @@ class PhoneHubTrayTest : public AshTestBase {
protected: protected:
PhoneHubTray* phone_hub_tray_ = nullptr; PhoneHubTray* phone_hub_tray_ = nullptr;
chromeos::phonehub::FakePhoneHubManager phone_hub_manager_; chromeos::phonehub::FakePhoneHubManager phone_hub_manager_;
base::test::ScopedFeatureList feature_list_;
MockNewWindowDelegate new_window_delegate_; MockNewWindowDelegate new_window_delegate_;
base::test::ScopedFeatureList feature_list_;
}; };
TEST_F(PhoneHubTrayTest, SetPhoneHubManager) { TEST_F(PhoneHubTrayTest, SetPhoneHubManager) {
// Set a new manager. // Set a new manager.
chromeos::phonehub::FakePhoneHubManager new_manager; chromeos::phonehub::FakePhoneHubManager new_manager;
new_manager.fake_feature_status_provider()->SetStatus( new_manager.fake_feature_status_provider()->SetStatus(
chromeos::phonehub::FeatureStatus::kEnabledAndConnected); chromeos::phonehub::FeatureStatus::kEligiblePhoneButNotSetUp);
phone_hub_tray_->SetPhoneHubManager(&new_manager); phone_hub_tray_->SetPhoneHubManager(&new_manager);
EXPECT_TRUE(phone_hub_tray_->GetVisible()); EXPECT_TRUE(phone_hub_tray_->GetVisible());
// Changing the old manager should have no effect. // Changing the old manager should have no effect.
GetFeatureStatusProvider()->SetStatus( GetFeatureStatusProvider()->SetStatus(
chromeos::phonehub::FeatureStatus::kNotEligibleForFeature); chromeos::phonehub::FeatureStatus::kDisabled);
EXPECT_TRUE(phone_hub_tray_->GetVisible()); EXPECT_TRUE(phone_hub_tray_->GetVisible());
// Only the new manager should work. // Only the new manager should work.
new_manager.fake_feature_status_provider()->SetStatus( new_manager.fake_feature_status_provider()->SetStatus(
chromeos::phonehub::FeatureStatus::kNotEligibleForFeature); chromeos::phonehub::FeatureStatus::kDisabled);
EXPECT_FALSE(phone_hub_tray_->GetVisible()); EXPECT_FALSE(phone_hub_tray_->GetVisible());
// Set no manager. // Set no manager.
...@@ -167,33 +166,4 @@ TEST_F(PhoneHubTrayTest, StartNotificationSetUpFlow) { ...@@ -167,33 +166,4 @@ TEST_F(PhoneHubTrayTest, StartNotificationSetUpFlow) {
ClickOnAndWait(notification_opt_in_view()->set_up_button_for_testing()); ClickOnAndWait(notification_opt_in_view()->set_up_button_for_testing());
} }
TEST_F(PhoneHubTrayTest, HideTrayItemOnUiStateChange) {
ClickTrayButton();
EXPECT_TRUE(phone_hub_tray_->is_active());
GetFeatureStatusProvider()->SetStatus(
chromeos::phonehub::FeatureStatus::kNotEligibleForFeature);
EXPECT_FALSE(phone_hub_tray_->is_active());
EXPECT_FALSE(phone_hub_tray_->GetVisible());
}
TEST_F(PhoneHubTrayTest, TransitionContentView) {
ClickTrayButton();
EXPECT_TRUE(phone_hub_tray_->is_active());
auto* content_view = phone_hub_tray_->content_view_for_testing();
EXPECT_TRUE(content_view);
// TODO(tengs) Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
GetFeatureStatusProvider()->SetStatus(
chromeos::phonehub::FeatureStatus::kEnabledButDisconnected);
content_view = phone_hub_tray_->content_view_for_testing();
EXPECT_TRUE(content_view);
// TODO(tengs) Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
} // namespace ash } // namespace ash
// 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 "ash/system/phonehub/phone_hub_ui_controller.h"
#include "ash/system/phonehub/bluetooth_disabled_view.h"
#include "ash/system/phonehub/connection_error_view.h"
#include "ash/system/phonehub/initial_connecting_view.h"
#include "ash/system/phonehub/onboarding_view.h"
#include "ash/system/phonehub/phone_connected_view.h"
#include "ash/system/phonehub/phone_status_view.h"
#include "base/logging.h"
#include "chromeos/components/phonehub/phone_hub_manager.h"
using FeatureStatus = chromeos::phonehub::FeatureStatus;
namespace ash {
PhoneHubUiController::PhoneHubUiController() = default;
PhoneHubUiController::~PhoneHubUiController() {
CleanUpPhoneHubManager();
}
void PhoneHubUiController::SetPhoneHubManager(
chromeos::phonehub::PhoneHubManager* phone_hub_manager) {
if (phone_hub_manager == phone_hub_manager_)
return;
CleanUpPhoneHubManager();
phone_hub_manager_ = phone_hub_manager;
if (phone_hub_manager_) {
phone_hub_manager_->GetFeatureStatusProvider()->AddObserver(this);
phone_hub_manager_->GetOnboardingUiTracker()->AddObserver(this);
}
UpdateUiState();
}
std::unique_ptr<views::View> PhoneHubUiController::CreateStatusHeaderView() {
if (!phone_hub_manager_)
return nullptr;
return std::make_unique<PhoneStatusView>(phone_hub_manager_->GetPhoneModel());
}
std::unique_ptr<views::View> PhoneHubUiController::CreateContentView(
TrayBubbleView* bubble_view) {
switch (ui_state_) {
case UiState::kHidden:
return nullptr;
case UiState::kOnboardingWithoutPhone:
// TODO(tengs): distinguish this onboarding with phone state.
FALLTHROUGH;
case UiState::kOnboardingWithPhone:
return std::make_unique<OnboardingView>();
case UiState::kBluetoothDisabled:
return std::make_unique<BluetoothDisabledView>();
case UiState::kInitialConnecting:
return std::make_unique<InitialConnectingView>();
case UiState::kPhoneConnecting:
return std::make_unique<ConnectionErrorView>(
ConnectionErrorView::ErrorStatus::kReconnecting);
case UiState::kConnectionError:
return std::make_unique<ConnectionErrorView>(
ConnectionErrorView::ErrorStatus::kDisconnected);
case UiState::kPhoneConnected:
return std::make_unique<PhoneConnectedView>(bubble_view,
phone_hub_manager_);
}
}
void PhoneHubUiController::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void PhoneHubUiController::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void PhoneHubUiController::OnFeatureStatusChanged() {
UpdateUiState();
}
void PhoneHubUiController::OnShouldShowOnboardingUiChanged() {
UpdateUiState();
}
void PhoneHubUiController::UpdateUiState() {
auto new_state = GetUiStateFromPhoneHubManager();
if (new_state == ui_state_)
return;
ui_state_ = new_state;
for (auto& observer : observer_list_)
observer.OnPhoneHubUiStateChanged();
}
PhoneHubUiController::UiState
PhoneHubUiController::GetUiStateFromPhoneHubManager() {
if (!phone_hub_manager_)
return UiState::kHidden;
auto feature_status =
phone_hub_manager_->GetFeatureStatusProvider()->GetStatus();
auto* tracker = phone_hub_manager_->GetOnboardingUiTracker();
bool should_show_onboarding_ui = tracker->ShouldShowOnboardingUi();
switch (feature_status) {
case FeatureStatus::kNotEligibleForFeature:
return UiState::kHidden;
case FeatureStatus::kEligiblePhoneButNotSetUp:
return should_show_onboarding_ui ? UiState::kOnboardingWithPhone
: UiState::kHidden;
case FeatureStatus::kDisabled:
return should_show_onboarding_ui ? UiState::kOnboardingWithoutPhone
: UiState::kHidden;
case FeatureStatus::kPhoneSelectedAndPendingSetup:
return UiState::kInitialConnecting;
case FeatureStatus::kUnavailableBluetoothOff:
return UiState::kBluetoothDisabled;
case FeatureStatus::kEnabledButDisconnected:
return UiState::kConnectionError;
case FeatureStatus::kEnabledAndConnecting:
return UiState::kPhoneConnecting;
case FeatureStatus::kEnabledAndConnected:
return UiState::kPhoneConnected;
}
}
void PhoneHubUiController::CleanUpPhoneHubManager() {
if (!phone_hub_manager_)
return;
phone_hub_manager_->GetFeatureStatusProvider()->RemoveObserver(this);
phone_hub_manager_->GetOnboardingUiTracker()->RemoveObserver(this);
}
} // namespace ash
// 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 ASH_SYSTEM_PHONEHUB_PHONE_HUB_UI_CONTROLLER_H_
#define ASH_SYSTEM_PHONEHUB_PHONE_HUB_UI_CONTROLLER_H_
#include "ash/ash_export.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "chromeos/components/phonehub/feature_status_provider.h"
#include "chromeos/components/phonehub/onboarding_ui_tracker.h"
namespace chromeos {
namespace phonehub {
class PhoneHubManager;
} // namespace phonehub
} // namespace chromeos
namespace views {
class View;
} // namespace views
namespace ash {
class TrayBubbleView;
// This controller translates the state received from PhoneHubManager into the
// corresponding main content view to be displayed in the tray bubble.
class ASH_EXPORT PhoneHubUiController
: public chromeos::phonehub::FeatureStatusProvider::Observer,
public chromeos::phonehub::OnboardingUiTracker::Observer {
public:
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
virtual void OnPhoneHubUiStateChanged() = 0;
};
// All the possible states that the main content view can be in. Each state
// has a corresponding view class.
enum class UiState {
kHidden = 0,
kOnboardingWithoutPhone,
kOnboardingWithPhone,
kBluetoothDisabled,
kInitialConnecting,
kPhoneConnecting,
kConnectionError,
kPhoneConnected,
};
PhoneHubUiController();
PhoneHubUiController(const PhoneHubUiController&) = delete;
~PhoneHubUiController() override;
PhoneHubUiController& operator=(const PhoneHubUiController&) = delete;
// Sets the PhoneHubManager that provides the data to drive the UI.
void SetPhoneHubManager(
chromeos::phonehub::PhoneHubManager* phone_hub_manager);
// Creates the corresponding content view for the current UI state.
// |bubble_view| will be the parent the created content view.
std::unique_ptr<views::View> CreateContentView(TrayBubbleView* bubble_view);
// Creates the header view displaying the phone status.
std::unique_ptr<views::View> CreateStatusHeaderView();
// Observer functions.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
UiState ui_state() const { return ui_state_; }
private:
// chromeos::phonehub::FeatureStatusProvider::Observer:
void OnFeatureStatusChanged() override;
// chromeos::phonehub::OnboardingUiTracker::Observer:
void OnShouldShowOnboardingUiChanged() override;
// Updates the current UI state and notifies observers.
void UpdateUiState();
// Returns the UiState from the PhoneHubManager.
UiState GetUiStateFromPhoneHubManager();
// Cleans up |phone_hub_manager_| by removing all observers.
void CleanUpPhoneHubManager();
// The PhoneHubManager that provides data for the UI.
chromeos::phonehub::PhoneHubManager* phone_hub_manager_ = nullptr;
// The current UI state.
UiState ui_state_ = UiState::kHidden;
// Registered observers.
base::ObserverList<Observer> observer_list_;
};
} // namespace ash
#endif // ASH_SYSTEM_PHONEHUB_PHONE_HUB_UI_CONTROLLER_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 "ash/system/phonehub/phone_hub_ui_controller.h"
#include "ash/test/ash_test_base.h"
#include "chromeos/components/phonehub/fake_phone_hub_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/view.h"
using FeatureStatus = chromeos::phonehub::FeatureStatus;
namespace ash {
class PhoneHubUiControllerTest : public AshTestBase,
public PhoneHubUiController::Observer {
public:
PhoneHubUiControllerTest() = default;
~PhoneHubUiControllerTest() override { controller_.RemoveObserver(this); }
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
controller_.AddObserver(this);
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kEnabledAndConnected);
GetOnboardingUiTracker()->SetShouldShowOnboardingUi(false);
controller_.SetPhoneHubManager(&phone_hub_manager_);
CHECK(ui_state_changed_);
ui_state_changed_ = false;
}
chromeos::phonehub::FakeFeatureStatusProvider* GetFeatureStatusProvider() {
return phone_hub_manager_.fake_feature_status_provider();
}
chromeos::phonehub::FakeOnboardingUiTracker* GetOnboardingUiTracker() {
return phone_hub_manager_.fake_onboarding_ui_tracker();
}
protected:
// PhoneHubUiController::Observer:
void OnPhoneHubUiStateChanged() override {
CHECK(!ui_state_changed_);
ui_state_changed_ = true;
}
PhoneHubUiController controller_;
chromeos::phonehub::FakePhoneHubManager phone_hub_manager_;
bool ui_state_changed_ = false;
};
TEST_F(PhoneHubUiControllerTest, NotEligibleForFeature) {
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kNotEligibleForFeature);
EXPECT_EQ(PhoneHubUiController::UiState::kHidden, controller_.ui_state());
EXPECT_TRUE(ui_state_changed_);
EXPECT_FALSE(controller_.CreateContentView(/*bubble_view=*/nullptr).get());
}
TEST_F(PhoneHubUiControllerTest, OnboardingNotEligible) {
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kDisabled);
EXPECT_EQ(PhoneHubUiController::UiState::kHidden, controller_.ui_state());
EXPECT_FALSE(controller_.CreateContentView(/*bubble_view=*/nullptr).get());
}
TEST_F(PhoneHubUiControllerTest, ShowOnboardingUi_WithoutPhone) {
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kDisabled);
EXPECT_TRUE(ui_state_changed_);
ui_state_changed_ = false;
GetOnboardingUiTracker()->SetShouldShowOnboardingUi(true);
EXPECT_TRUE(ui_state_changed_);
EXPECT_EQ(PhoneHubUiController::UiState::kOnboardingWithoutPhone,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
TEST_F(PhoneHubUiControllerTest, ShowOnboardingUi_WithPhone) {
GetFeatureStatusProvider()->SetStatus(
FeatureStatus::kEligiblePhoneButNotSetUp);
EXPECT_TRUE(ui_state_changed_);
ui_state_changed_ = false;
GetOnboardingUiTracker()->SetShouldShowOnboardingUi(true);
EXPECT_TRUE(ui_state_changed_);
EXPECT_EQ(PhoneHubUiController::UiState::kOnboardingWithPhone,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
TEST_F(PhoneHubUiControllerTest, PhoneConnectingForOnboarding) {
GetFeatureStatusProvider()->SetStatus(
FeatureStatus::kPhoneSelectedAndPendingSetup);
EXPECT_EQ(PhoneHubUiController::UiState::kInitialConnecting,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
TEST_F(PhoneHubUiControllerTest, BluetoothOff) {
GetFeatureStatusProvider()->SetStatus(
FeatureStatus::kUnavailableBluetoothOff);
EXPECT_EQ(PhoneHubUiController::UiState::kBluetoothDisabled,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
TEST_F(PhoneHubUiControllerTest, PhoneDisconnected) {
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kEnabledButDisconnected);
EXPECT_EQ(PhoneHubUiController::UiState::kConnectionError,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
TEST_F(PhoneHubUiControllerTest, PhoneConnecting) {
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kEnabledAndConnecting);
EXPECT_EQ(PhoneHubUiController::UiState::kPhoneConnecting,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
TEST_F(PhoneHubUiControllerTest, PhoneConnected) {
GetFeatureStatusProvider()->SetStatus(FeatureStatus::kEnabledAndConnected);
EXPECT_EQ(PhoneHubUiController::UiState::kPhoneConnected,
controller_.ui_state());
auto content_view = controller_.CreateContentView(/*bubble_view=*/nullptr);
// TODO(tengs): Test the actual view id.
EXPECT_EQ(0, content_view->GetID());
}
} // namespace ash
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