Avi Drissman | 3a215d1e | 2022-09-07 19:43:09 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 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" |
minch | 9cfa2b0 | 2021-11-12 20:42:41 | [diff] [blame] | 18 | #include "ash/style/pill_button.h" |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 19 | #include "ash/system/model/system_tray_model.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" |
Avi Drissman | 4de6dab | 2023-01-06 23:17:35 | [diff] [blame] | 27 | #include "base/functional/bind.h" |
Arthur Sonzogni | 834e018f | 2023-04-22 10:20:02 | [diff] [blame] | 28 | #include "base/memory/raw_ptr.h" |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 29 | #include "base/strings/strcat.h" |
Henrique Ferreiro | fadda06 | 2022-12-01 16:27:14 | [diff] [blame] | 30 | #include "chromeos/ash/components/phonehub/onboarding_ui_tracker.h" |
Crisrael Lucero | f1392ae | 2023-05-12 23:17:45 | [diff] [blame] | 31 | #include "chromeos/constants/chromeos_features.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 32 | #include "ui/base/l10n/l10n_util.h" |
Allen Bauer | a66b3eed | 2021-05-05 02:42:12 | [diff] [blame] | 33 | #include "ui/base/metadata/metadata_impl_macros.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 34 | #include "ui/base/resource/resource_bundle.h" |
Andre Le | 09e38cd | 2020-11-05 19:49:16 | [diff] [blame] | 35 | #include "ui/chromeos/devicetype_utils.h" |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 36 | #include "ui/views/layout/fill_layout.h" |
| 37 | |
| 38 | namespace ash { |
| 39 | |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 40 | using phone_hub_metrics::InterstitialScreenEvent; |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 41 | using phone_hub_metrics::Screen; |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 42 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 43 | // OnboardingMainView --------------------------------------------------------- |
| 44 | // Main onboarding screen with Phone Hub feature description and two buttons |
| 45 | // (Get Started and Dismiss), where user can either choose to grant permission |
| 46 | // to enable this feature or dismiss the screen. |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 47 | class OnboardingMainView : public PhoneHubInterstitialView { |
David Yeung | adb2744 | 2023-11-14 22:49:51 | [diff] [blame^] | 48 | METADATA_HEADER(OnboardingMainView, PhoneHubInterstitialView) |
| 49 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 50 | public: |
Henrique Ferreiro | 91fb61df | 2021-11-12 15:51:03 | [diff] [blame] | 51 | OnboardingMainView(phonehub::OnboardingUiTracker* onboarding_ui_tracker, |
| 52 | OnboardingView* parent_view, |
| 53 | OnboardingView::OnboardingFlow onboarding_flow) |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 54 | : PhoneHubInterstitialView(/*show_progress=*/false), |
| 55 | onboarding_ui_tracker_(onboarding_ui_tracker), |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 56 | parent_view_(parent_view), |
| 57 | onboarding_flow_(onboarding_flow) { |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 58 | SetID(PhoneHubViewID::kOnboardingMainView); |
| 59 | InitLayout(); |
| 60 | } |
| 61 | |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 62 | // PhoneHubInterstitialView: |
| 63 | Screen GetScreenForMetrics() const override { |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 64 | switch (onboarding_flow_) { |
| 65 | case OnboardingView::kExistingMultideviceUser: |
| 66 | return Screen::kOnboardingExistingMultideviceUser; |
| 67 | case OnboardingView::kNewMultideviceUser: |
| 68 | return Screen::kOnboardingNewMultideviceUser; |
| 69 | } |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 70 | } |
| 71 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 72 | private: |
| 73 | void InitLayout() { |
Avery Musbach | 5ebb29fa | 2022-01-28 17:12:48 | [diff] [blame] | 74 | SetImage(ui::ResourceBundle::GetSharedInstance().GetThemedLottieImageNamed( |
| 75 | IDR_PHONE_HUB_ONBOARDING_IMAGE)); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 76 | SetTitle( |
| 77 | l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_TITLE)); |
Kyle Horimoto | fe7173b | 2021-01-14 22:32:48 | [diff] [blame] | 78 | SetDescription(l10n_util::GetStringUTF16( |
| 79 | IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_DESCRIPTION)); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 80 | |
| 81 | // Add "Dismiss" and "Get started" buttons. |
Crisrael Lucero | f1392ae | 2023-05-12 23:17:45 | [diff] [blame] | 82 | // TODO(b/281844561): Migrate the "Dismiss" button to use |
| 83 | // |PillButton::Type::kSecondaryWithoutIcon| when the PillButton colors |
| 84 | // are updated with better contrast-ratios. |
minch | a067493 | 2021-10-18 17:56:21 | [diff] [blame] | 85 | auto dismiss = std::make_unique<PillButton>( |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 86 | base::BindRepeating(&OnboardingMainView::DismissButtonPressed, |
| 87 | base::Unretained(this)), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 88 | l10n_util::GetStringUTF16( |
| 89 | IDS_ASH_PHONE_HUB_ONBOARDING_DIALOG_DISMISS_BUTTON), |
Xiaodan Zhu | e21746e | 2022-08-22 22:12:10 | [diff] [blame] | 90 | PillButton::Type::kFloatingWithoutIcon, /*icon=*/nullptr); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 91 | dismiss->SetID(PhoneHubViewID::kOnboardingDismissButton); |
| 92 | AddButton(std::move(dismiss)); |
| 93 | |
minch | a067493 | 2021-10-18 17:56:21 | [diff] [blame] | 94 | auto get_started = std::make_unique<PillButton>( |
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), |
Crisrael Lucero | f1392ae | 2023-05-12 23:17:45 | [diff] [blame] | 99 | chromeos::features::IsJellyrollEnabled() |
| 100 | ? PillButton::Type::kPrimaryWithoutIcon |
| 101 | : PillButton::Type::kDefaultWithoutIcon, |
| 102 | /*icon=*/nullptr); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 103 | get_started->SetID(PhoneHubViewID::kOnboardingGetStartedButton); |
| 104 | AddButton(std::move(get_started)); |
| 105 | } |
| 106 | |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 107 | void GetStartedButtonPressed() { |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 108 | LogInterstitialScreenEvent(InterstitialScreenEvent::kConfirm); |
Pu Shi | 0efed35f | 2023-08-07 19:21:24 | [diff] [blame] | 109 | onboarding_ui_tracker_->HandleGetStarted( |
| 110 | parent_view_->IsOnboardingViewStartedFromNudge()); |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | void DismissButtonPressed() { |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 114 | LogInterstitialScreenEvent(InterstitialScreenEvent::kDismiss); |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 115 | parent_view_->ShowDismissPrompt(); |
| 116 | } |
| 117 | |
Arthur Sonzogni | 834e018f | 2023-04-22 10:20:02 | [diff] [blame] | 118 | raw_ptr<phonehub::OnboardingUiTracker, ExperimentalAsh> |
| 119 | onboarding_ui_tracker_ = nullptr; |
| 120 | raw_ptr<OnboardingView, ExperimentalAsh> parent_view_ = nullptr; |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 121 | const OnboardingView::OnboardingFlow onboarding_flow_; |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 122 | }; |
| 123 | |
David Yeung | adb2744 | 2023-11-14 22:49:51 | [diff] [blame^] | 124 | BEGIN_METADATA(OnboardingMainView) |
| 125 | END_METADATA |
| 126 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 127 | // OnboardingDismissPromptView ------------------------------------------------ |
| 128 | // A follow-up prompt screen that pops up when the user has chosen to dismiss |
| 129 | // the main onboarding screen. It should not be shown again after being |
| 130 | // dismissed manually by either clicking the ack button or outside the bubble. |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 131 | class OnboardingDismissPromptView : public PhoneHubInterstitialView { |
David Yeung | adb2744 | 2023-11-14 22:49:51 | [diff] [blame^] | 132 | METADATA_HEADER(OnboardingDismissPromptView, PhoneHubInterstitialView) |
| 133 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 134 | public: |
| 135 | explicit OnboardingDismissPromptView( |
Henrique Ferreiro | 91fb61df | 2021-11-12 15:51:03 | [diff] [blame] | 136 | phonehub::OnboardingUiTracker* onboarding_ui_tracker) |
Meilin Wang | ffecec38 | 2020-11-13 03:12:23 | [diff] [blame] | 137 | : PhoneHubInterstitialView(/*show_progress=*/false, /*show_image=*/false), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 138 | onboarding_ui_tracker_(onboarding_ui_tracker) { |
| 139 | SetID(PhoneHubViewID::kOnboardingDismissPromptView); |
| 140 | InitLayout(); |
| 141 | } |
| 142 | |
| 143 | private: |
| 144 | void InitLayout() { |
| 145 | // Adds title and description. |
| 146 | SetTitle(l10n_util::GetStringUTF16( |
| 147 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_TITLE)); |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame] | 148 | std::u16string part1 = l10n_util::GetStringUTF16( |
Kyle Horimoto | fe7173b | 2021-01-14 22:32:48 | [diff] [blame] | 149 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_DESCRIPTION_PART_1); |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame] | 150 | std::u16string part2 = l10n_util::GetStringUTF16( |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 151 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_DESCRIPTION_PART_2); |
| 152 | // Uses "\n" to create a newline separator between two text paragraphs. |
Jan Wilken Dörrie | c92a6d724 | 2021-03-23 17:43:48 | [diff] [blame] | 153 | SetDescription(base::StrCat({part1, u"\n\n", part2})); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 154 | |
| 155 | // Adds "Ok, got it" button. |
minch | a067493 | 2021-10-18 17:56:21 | [diff] [blame] | 156 | auto ack_button = std::make_unique<PillButton>( |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 157 | base::BindRepeating(&OnboardingDismissPromptView::ButtonPressed, |
| 158 | base::Unretained(this)), |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 159 | l10n_util::GetStringUTF16( |
| 160 | IDS_ASH_PHONE_HUB_ONBOARDING_DISMISS_DIALOG_OK_BUTTON), |
Xiaodan Zhu | e21746e | 2022-08-22 22:12:10 | [diff] [blame] | 161 | PillButton::Type::kDefaultWithoutIcon, /*icon=*/nullptr); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 162 | ack_button->SetID(PhoneHubViewID::kOnboardingDismissAckButton); |
| 163 | AddButton(std::move(ack_button)); |
| 164 | } |
| 165 | |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 166 | void ButtonPressed() { |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 167 | LogInterstitialScreenEvent(InterstitialScreenEvent::kConfirm); |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 168 | |
Andre Le | 4a5d5cd | 2020-12-08 20:04:15 | [diff] [blame] | 169 | // Close Phone Hub bubble in current display. |
| 170 | views::Widget* const widget = GetWidget(); |
| 171 | // |widget| is null when this function is called before the view is added to |
| 172 | // a widget (in unit tests). |
| 173 | if (!widget) |
| 174 | return; |
| 175 | int64_t current_display_id = |
| 176 | display::Screen::GetScreen() |
| 177 | ->GetDisplayNearestWindow(widget->GetNativeWindow()) |
| 178 | .id(); |
| 179 | Shell::GetRootWindowControllerWithDisplayId(current_display_id) |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 180 | ->GetStatusAreaWidget() |
| 181 | ->phone_hub_tray() |
| 182 | ->CloseBubble(); |
| 183 | } |
| 184 | |
Peter Kasting | 5aacfd49 | 2020-10-31 03:33:14 | [diff] [blame] | 185 | // PhoneHubInterstitialView: |
| 186 | void OnBubbleClose() override { onboarding_ui_tracker_->DismissSetupUi(); } |
| 187 | |
| 188 | Screen GetScreenForMetrics() const override { |
| 189 | return Screen::kOnboardingDismissPrompt; |
| 190 | } |
| 191 | |
Arthur Sonzogni | 834e018f | 2023-04-22 10:20:02 | [diff] [blame] | 192 | raw_ptr<phonehub::OnboardingUiTracker, ExperimentalAsh> |
| 193 | onboarding_ui_tracker_ = nullptr; |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 194 | }; |
| 195 | |
David Yeung | adb2744 | 2023-11-14 22:49:51 | [diff] [blame^] | 196 | BEGIN_METADATA(OnboardingDismissPromptView) |
| 197 | END_METADATA |
| 198 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 199 | // OnboardingView ------------------------------------------------------------- |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 200 | OnboardingView::OnboardingView( |
Henrique Ferreiro | 91fb61df | 2021-11-12 15:51:03 | [diff] [blame] | 201 | phonehub::OnboardingUiTracker* onboarding_ui_tracker, |
Meilin Wang | 6531973 | 2020-11-06 20:03:18 | [diff] [blame] | 202 | Delegate* delegate, |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 203 | OnboardingFlow onboarding_flow) |
Meilin Wang | 6531973 | 2020-11-06 20:03:18 | [diff] [blame] | 204 | : onboarding_ui_tracker_(onboarding_ui_tracker), delegate_(delegate) { |
Meilin Wang | 98efd1be | 2020-10-01 22:25:35 | [diff] [blame] | 205 | SetID(PhoneHubViewID::kOnboardingView); |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 206 | |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 207 | SetLayoutManager(std::make_unique<views::FillLayout>()); |
Tim Song | 5a9883f5 | 2020-11-04 19:40:03 | [diff] [blame] | 208 | main_view_ = AddChildView(std::make_unique<OnboardingMainView>( |
| 209 | onboarding_ui_tracker_, this, onboarding_flow)); |
Tim Song | 8530370 | 2020-10-16 04:57:00 | [diff] [blame] | 210 | |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 211 | LogInterstitialScreenEvent(InterstitialScreenEvent::kShown); |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | OnboardingView::~OnboardingView() = default; |
| 215 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 216 | void OnboardingView::OnBubbleClose() { |
| 217 | main_view_->OnBubbleClose(); |
| 218 | } |
| 219 | |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 220 | Screen OnboardingView::GetScreenForMetrics() const { |
| 221 | return main_view_->GetScreenForMetrics(); |
| 222 | } |
| 223 | |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 224 | void OnboardingView::ShowDismissPrompt() { |
| 225 | DCHECK(main_view_); |
| 226 | |
Tim Song | a6682b15 | 2020-11-03 21:24:24 | [diff] [blame] | 227 | LogInterstitialScreenEvent(InterstitialScreenEvent::kShown); |
Tim Song | 947932a | 2020-10-31 00:38:45 | [diff] [blame] | 228 | |
Arthur Sonzogni | 834e018f | 2023-04-22 10:20:02 | [diff] [blame] | 229 | RemoveChildViewT(main_view_.get()); |
Meilin Wang | d626fde | 2020-10-23 17:53:36 | [diff] [blame] | 230 | main_view_ = AddChildView( |
| 231 | std::make_unique<OnboardingDismissPromptView>(onboarding_ui_tracker_)); |
| 232 | |
Meilin Wang | 6531973 | 2020-11-06 20:03:18 | [diff] [blame] | 233 | // We don't show status header view on top for the dismiss prompt. |
| 234 | DCHECK(delegate_); |
| 235 | delegate_->HideStatusHeaderView(); |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 236 | } |
| 237 | |
Pu Shi | 0efed35f | 2023-08-07 19:21:24 | [diff] [blame] | 238 | bool OnboardingView::IsOnboardingViewStartedFromNudge() { |
| 239 | if (!delegate_) { |
| 240 | return false; |
| 241 | } |
| 242 | |
| 243 | return delegate_->IsPhoneHubIconClickedWhenNudgeVisible(); |
| 244 | } |
| 245 | |
Meilin Wang | 418ae71 | 2020-09-17 05:50:03 | [diff] [blame] | 246 | BEGIN_METADATA(OnboardingView, views::View) |
| 247 | END_METADATA |
| 248 | |
| 249 | } // namespace ash |