Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "ash/system/phonehub/onboarding_view.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <memory> |
Jan Wilken Dörrie | ad587c3 | 2021-03-11 14:09:27 | [diff] [blame] | 9 | #include <string> |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 10 | #include <vector> |
| 11 | |
| 12 | #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h" |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 13 | #include "ash/public/cpp/system_tray_client.h" |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 14 | #include "ash/root_window_controller.h" |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 15 | #include "ash/shell.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 16 | #include "ash/strings/grit/ash_strings.h" |
| 17 | #include "ash/style/ash_color_provider.h" |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 18 | #include "ash/system/model/system_tray_model.h" |
Meilin Wang | 3062ff2e | 2020-10-07 22:26:43 | [diff] [blame] | 19 | #include "ash/system/phonehub/interstitial_view_button.h" |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 20 | #include "ash/system/phonehub/phone_hub_content_view.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 21 | #include "ash/system/phonehub/phone_hub_interstitial_view.h" |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 22 | #include "ash/system/phonehub/phone_hub_metrics.h" |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 23 | #include "ash/system/phonehub/phone_hub_tray.h" |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 24 | #include "ash/system/phonehub/phone_hub_view_ids.h" |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 25 | #include "ash/system/status_area_widget.h" |
| 26 | #include "ash/system/tray/tray_bubble_view.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 27 | #include "ash/system/unified/rounded_label_button.h" |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 28 | #include "base/strings/strcat.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 29 | #include "base/strings/string16.h" |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 30 | #include "chromeos/components/phonehub/onboarding_ui_tracker.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 31 | #include "ui/base/l10n/l10n_util.h" |
| 32 | #include "ui/base/resource/resource_bundle.h" |
Andre Le | 09e38cd | 2020-11-05 19:49:16 | [diff] [blame] | 33 | #include "ui/chromeos/devicetype_utils.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 34 | #include "ui/views/layout/fill_layout.h" |
Wei Li | 5add541 | 2020-10-15 15:57:21 | [diff] [blame] | 35 | #include "ui/views/metadata/metadata_impl_macros.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 36 | |
| 37 | namespace ash { |
| 38 | |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 39 | using phone_hub_metrics::InterstitialScreenEvent; |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 40 | using phone_hub_metrics::Screen; |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 41 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 42 | // OnboardingMainView --------------------------------------------------------- |
| 43 | // Main onboarding screen with Phone Hub feature description and two buttons |
| 44 | // (Get Started and Dismiss), where user can either choose to grant permission |
| 45 | // to enable this feature or dismiss the screen. |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 46 | class OnboardingMainView : public PhoneHubInterstitialView { |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 47 | public: |
| 48 | OnboardingMainView( |
| 49 | chromeos::phonehub::OnboardingUiTracker* onboarding_ui_tracker, |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 50 | OnboardingView* parent_view, |
| 51 | OnboardingView::OnboardingFlow onboarding_flow) |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 52 | : PhoneHubInterstitialView(/*show_progress=*/false), |
| 53 | onboarding_ui_tracker_(onboarding_ui_tracker), |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 54 | parent_view_(parent_view), |
| 55 | onboarding_flow_(onboarding_flow) { |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 56 | SetID(PhoneHubViewID::kOnboardingMainView); |
| 57 | InitLayout(); |
| 58 | } |
| 59 | |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 60 | // PhoneHubInterstitialView: |
| 61 | Screen GetScreenForMetrics() const override { |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 62 | switch (onboarding_flow_) { |
| 63 | case OnboardingView::kExistingMultideviceUser: |
| 64 | return Screen::kOnboardingExistingMultideviceUser; |
| 65 | case OnboardingView::kNewMultideviceUser: |
| 66 | return Screen::kOnboardingNewMultideviceUser; |
| 67 | } |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 68 | } |
| 69 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 70 | private: |
| 71 | void InitLayout() { |
| 72 | // TODO(crbug.com/1127996): Replace PNG file with vector icon. |
| 73 | gfx::ImageSkia* image = |
| 74 | ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| 75 | IDR_PHONE_HUB_ONBOARDING_IMAGE); |
| 76 | SetImage(*image); |
| 77 | SetTitle( |
| 78 | l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_TITLE)); |
Kyle Horimoto | fe7173b | 2021-01-14 22:32:48 | [diff] [blame] | 79 | SetDescription(l10n_util::GetStringUTF16( |
| 80 | IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_DESCRIPTION)); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 81 | |
| 82 | // Add "Dismiss" and "Get started" buttons. |
| 83 | auto dismiss = std::make_unique<InterstitialViewButton>( |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 84 | base::BindRepeating(&OnboardingMainView::DismissButtonPressed, |
| 85 | base::Unretained(this)), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 86 | l10n_util::GetStringUTF16( |
| 87 | IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_DISMISS_BUTTON), |
| 88 | /*paint_background=*/false); |
| 89 | dismiss->SetEnabledTextColors(AshColorProvider::Get()->GetContentLayerColor( |
| 90 | AshColorProvider::ContentLayerType::kTextColorPrimary)); |
| 91 | dismiss->SetID(PhoneHubViewID::kOnboardingDismissButton); |
| 92 | AddButton(std::move(dismiss)); |
| 93 | |
| 94 | auto get_started = std::make_unique<InterstitialViewButton>( |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 95 | base::BindRepeating(&OnboardingMainView::GetStartedButtonPressed, |
| 96 | base::Unretained(this)), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 97 | l10n_util::GetStringUTF16( |
| 98 | IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_GET_STARTED_BUTTON), |
| 99 | /*paint_background=*/true); |
| 100 | get_started->SetID(PhoneHubViewID::kOnboardingGetStartedButton); |
| 101 | AddButton(std::move(get_started)); |
| 102 | } |
| 103 | |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 104 | void GetStartedButtonPressed() { |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 105 | LogInterstitialScreenEvent(InterstitialScreenEvent::kConfirm); |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 106 | onboarding_ui_tracker_->HandleGetStarted(); |
| 107 | } |
| 108 | |
| 109 | void DismissButtonPressed() { |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 110 | LogInterstitialScreenEvent(InterstitialScreenEvent::kDismiss); |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 111 | parent_view_->ShowDismissPrompt(); |
| 112 | } |
| 113 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 114 | chromeos::phonehub::OnboardingUiTracker* onboarding_ui_tracker_ = nullptr; |
| 115 | OnboardingView* parent_view_ = nullptr; |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 116 | const OnboardingView::OnboardingFlow onboarding_flow_; |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 117 | }; |
| 118 | |
| 119 | // OnboardingDismissPromptView ------------------------------------------------ |
| 120 | // A follow-up prompt screen that pops up when the user has chosen to dismiss |
| 121 | // the main onboarding screen. It should not be shown again after being |
| 122 | // dismissed manually by either clicking the ack button or outside the bubble. |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 123 | class OnboardingDismissPromptView : public PhoneHubInterstitialView { |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 124 | public: |
| 125 | explicit OnboardingDismissPromptView( |
| 126 | chromeos::phonehub::OnboardingUiTracker* onboarding_ui_tracker) |
Meilin Wang | ffecec38 | 2020-11-13 03:12:23 | [diff] [blame] | 127 | : PhoneHubInterstitialView(/*show_progress=*/false, /*show_image=*/false), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 128 | onboarding_ui_tracker_(onboarding_ui_tracker) { |
| 129 | SetID(PhoneHubViewID::kOnboardingDismissPromptView); |
| 130 | InitLayout(); |
| 131 | } |
| 132 | |
| 133 | private: |
| 134 | void InitLayout() { |
| 135 | // Adds title and description. |
| 136 | SetTitle(l10n_util::GetStringUTF16( |
| 137 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_TITLE)); |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 138 | std::u16string part1 = l10n_util::GetStringUTF16( |
Kyle Horimoto | fe7173b | 2021-01-14 22:32:48 | [diff] [blame] | 139 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_DESCRIPTION_PART_1); |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 140 | std::u16string part2 = l10n_util::GetStringUTF16( |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 141 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_DESCRIPTION_PART_2); |
| 142 | // Uses "\n" to create a newline separator between two text paragraphs. |
| 143 | SetDescription(base::StrCat({part1, base::ASCIIToUTF16("\n\n"), part2})); |
| 144 | |
| 145 | // Adds "Ok, got it" button. |
| 146 | auto ack_button = std::make_unique<InterstitialViewButton>( |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 147 | base::BindRepeating(&OnboardingDismissPromptView::ButtonPressed, |
| 148 | base::Unretained(this)), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 149 | l10n_util::GetStringUTF16( |
| 150 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_OK_BUTTON), |
| 151 | /*paint_background=*/true); |
| 152 | ack_button->SetID(PhoneHubViewID::kOnboardingDismissAckButton); |
| 153 | AddButton(std::move(ack_button)); |
| 154 | } |
| 155 | |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 156 | void ButtonPressed() { |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 157 | LogInterstitialScreenEvent(InterstitialScreenEvent::kConfirm); |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 158 | |
Andre Le | 4a5d5cd | 2020-12-08 20:04:15 | [diff] [blame] | 159 | // Close Phone Hub bubble in current display. |
| 160 | views::Widget* const widget = GetWidget(); |
| 161 | // |widget| is null when this function is called before the view is added to |
| 162 | // a widget (in unit tests). |
| 163 | if (!widget) |
| 164 | return; |
| 165 | int64_t current_display_id = |
| 166 | display::Screen::GetScreen() |
| 167 | ->GetDisplayNearestWindow(widget->GetNativeWindow()) |
| 168 | .id(); |
| 169 | Shell::GetRootWindowControllerWithDisplayId(current_display_id) |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 170 | ->GetStatusAreaWidget() |
| 171 | ->phone_hub_tray() |
| 172 | ->CloseBubble(); |
| 173 | } |
| 174 | |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 175 | // PhoneHubInterstitialView: |
| 176 | void OnBubbleClose() override { onboarding_ui_tracker_->DismissSetupUi(); } |
| 177 | |
| 178 | Screen GetScreenForMetrics() const override { |
| 179 | return Screen::kOnboardingDismissPrompt; |
| 180 | } |
| 181 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 182 | chromeos::phonehub::OnboardingUiTracker* onboarding_ui_tracker_ = nullptr; |
| 183 | }; |
| 184 | |
| 185 | // OnboardingView ------------------------------------------------------------- |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 186 | OnboardingView::OnboardingView( |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 187 | chromeos::phonehub::OnboardingUiTracker* onboarding_ui_tracker, |
Meilin Wang | 6531973 | 2020-11-06 20:03:18 | [diff] [blame] | 188 | Delegate* delegate, |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 189 | OnboardingFlow onboarding_flow) |
Meilin Wang | 6531973 | 2020-11-06 20:03:18 | [diff] [blame] | 190 | : onboarding_ui_tracker_(onboarding_ui_tracker), delegate_(delegate) { |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 191 | SetID(PhoneHubViewID::kOnboardingView); |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 192 | |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 193 | SetLayoutManager(std::make_unique<views::FillLayout>()); |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 194 | main_view_ = AddChildView(std::make_unique<OnboardingMainView>( |
| 195 | onboarding_ui_tracker_, this, onboarding_flow)); |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 196 | |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 197 | LogInterstitialScreenEvent(InterstitialScreenEvent::kShown); |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | OnboardingView::~OnboardingView() = default; |
| 201 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 202 | void OnboardingView::OnBubbleClose() { |
| 203 | main_view_->OnBubbleClose(); |
| 204 | } |
| 205 | |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 206 | Screen OnboardingView::GetScreenForMetrics() const { |
| 207 | return main_view_->GetScreenForMetrics(); |
| 208 | } |
| 209 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 210 | void OnboardingView::ShowDismissPrompt() { |
| 211 | DCHECK(main_view_); |
| 212 | |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 213 | LogInterstitialScreenEvent(InterstitialScreenEvent::kShown); |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 214 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 215 | RemoveChildView(main_view_); |
| 216 | main_view_ = AddChildView( |
| 217 | std::make_unique<OnboardingDismissPromptView>(onboarding_ui_tracker_)); |
| 218 | |
Meilin Wang | 6531973 | 2020-11-06 20:03:18 | [diff] [blame] | 219 | // We don't show status header view on top for the dismiss prompt. |
| 220 | DCHECK(delegate_); |
| 221 | delegate_->HideStatusHeaderView(); |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 222 | } |
| 223 | |
| 224 | BEGIN_METADATA(OnboardingView, views::View) |
| 225 | END_METADATA |
| 226 | |
| 227 | } // namespace ash |