chromium/ash/assistant/ui/main_stage/assistant_onboarding_view.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/assistant/ui/main_stage/assistant_onboarding_view.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "ash/assistant/model/assistant_suggestions_model.h"
#include "ash/assistant/model/assistant_ui_model.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/assistant_view_delegate.h"
#include "ash/assistant/ui/assistant_view_ids.h"
#include "ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.h"
#include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
#include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_palette.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/table_layout_view.h"

namespace ash {

namespace {

using assistant::AssistantSuggestion;
using assistant::AssistantSuggestionType;

// Greeting.
constexpr int kGreetingLabelLineHeight = 28;
constexpr int kGreetingLabelSizeDelta = 10;

// Intro.
constexpr int kIntroLabelLineHeight = 20;
constexpr int kIntroLabelMarginTopDip = 12;
constexpr int kIntroLabelSizeDelta = 2;

// Suggestions.
constexpr int kSuggestionsColumnCount = 3;
constexpr int kSuggestionsMaxCount = 6;
constexpr int kSuggestionsMarginDip = 16;
constexpr int kSuggestionsMarginTopDip = 32;

// Helpers ---------------------------------------------------------------------

std::string GetGreetingMessage(AssistantViewDelegate* delegate) {
  base::Time::Exploded now;
  base::Time::Now().LocalExplode(&now);

  const std::string given_name = delegate->GetPrimaryUserGivenName();

  if (now.hour < 5) {
    return given_name.empty()
               ? l10n_util::GetStringUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_NIGHT_WITHOUT_NAME)
               : l10n_util::GetStringFUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_NIGHT,
                     base::UTF8ToUTF16(given_name));
  }

  if (now.hour < 12) {
    return given_name.empty()
               ? l10n_util::GetStringUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_MORNING_WITHOUT_NAME)
               : l10n_util::GetStringFUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_MORNING,
                     base::UTF8ToUTF16(given_name));
  }

  if (now.hour < 17) {
    return given_name.empty()
               ? l10n_util::GetStringUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_AFTERNOON_WITHOUT_NAME)
               : l10n_util::GetStringFUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_AFTERNOON,
                     base::UTF8ToUTF16(given_name));
  }

  if (now.hour < 23) {
    return given_name.empty()
               ? l10n_util::GetStringUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_EVENING_WITHOUT_NAME)
               : l10n_util::GetStringFUTF8(
                     IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_EVENING,
                     base::UTF8ToUTF16(given_name));
  }

  return given_name.empty()
             ? l10n_util::GetStringUTF8(
                   IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_NIGHT_WITHOUT_NAME)
             : l10n_util::GetStringFUTF8(
                   IDS_ASSISTANT_BETTER_ONBOARDING_GREETING_NIGHT,
                   base::UTF8ToUTF16(given_name));
}

}  // namespace

// AssistantOnboardingView -----------------------------------------------------

AssistantOnboardingView::AssistantOnboardingView(
    AssistantViewDelegate* delegate)
    : delegate_(delegate) {
  SetID(AssistantViewID::kOnboardingView);
  InitLayout();

  assistant_controller_observation_.Observe(AssistantController::Get());
  AssistantSuggestionsController::Get()->GetModel()->AddObserver(this);
  AssistantUiController::Get()->GetModel()->AddObserver(this);
}

AssistantOnboardingView::~AssistantOnboardingView() {
  if (AssistantUiController::Get())
    AssistantUiController::Get()->GetModel()->RemoveObserver(this);

  if (AssistantSuggestionsController::Get())
    AssistantSuggestionsController::Get()->GetModel()->RemoveObserver(this);
}

gfx::Size AssistantOnboardingView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  return gfx::Size(
      INT_MAX, GetLayoutManager()->GetPreferredHeightForWidth(this, INT_MAX));
}

void AssistantOnboardingView::ChildPreferredSizeChanged(views::View* child) {
  PreferredSizeChanged();
}

void AssistantOnboardingView::OnThemeChanged() {
  views::View::OnThemeChanged();

  SkColor greeting_color =
      GetColorProvider()->GetColor(kColorAshAssistantGreetingEnabled);
  greeting_->SetEnabledColor(greeting_color);
  intro_->SetEnabledColor(greeting_color);
}

void AssistantOnboardingView::OnAssistantControllerDestroying() {
  AssistantUiController::Get()->GetModel()->RemoveObserver(this);
  AssistantSuggestionsController::Get()->GetModel()->RemoveObserver(this);
  DCHECK(assistant_controller_observation_.IsObservingSource(
      AssistantController::Get()));
  assistant_controller_observation_.Reset();
}

void AssistantOnboardingView::OnOnboardingSuggestionsChanged(
    const std::vector<AssistantSuggestion>& onboarding_suggestions) {
  UpdateSuggestions();
}

void AssistantOnboardingView::OnUiVisibilityChanged(
    AssistantVisibility new_visibility,
    AssistantVisibility old_visibility,
    std::optional<AssistantEntryPoint> entry_point,
    std::optional<AssistantExitPoint> exit_point) {
  if (new_visibility != AssistantVisibility::kVisible)
    return;

  UpdateGreeting();

  if (IsDrawn())
    delegate_->OnOnboardingShown();
}

void AssistantOnboardingView::InitLayout() {
  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical,
      gfx::Insets::VH(0, assistant::ui::kHorizontalMargin)));

  // Greeting.
  greeting_ = AddChildView(std::make_unique<views::Label>());
  greeting_->SetAutoColorReadabilityEnabled(false);
  greeting_->SetFontList(assistant::ui::GetDefaultFontList()
                             .DeriveWithSizeDelta(kGreetingLabelSizeDelta)
                             .DeriveWithWeight(gfx::Font::Weight::MEDIUM));
  greeting_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  greeting_->SetLineHeight(kGreetingLabelLineHeight);
  greeting_->SetText(base::UTF8ToUTF16(GetGreetingMessage(delegate_)));

  // Intro.
  intro_ = AddChildView(std::make_unique<views::Label>());
  intro_->SetAutoColorReadabilityEnabled(false);
  intro_->SetBorder(views::CreateEmptyBorder(
      gfx::Insets::TLBR(kIntroLabelMarginTopDip, 0, 0, 0)));
  intro_->SetFontList(assistant::ui::GetDefaultFontList()
                          .DeriveWithSizeDelta(kIntroLabelSizeDelta)
                          .DeriveWithWeight(gfx::Font ::Weight::MEDIUM));
  intro_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  intro_->SetLineHeight(kIntroLabelLineHeight);
  intro_->SetMultiLine(true);
  intro_->SetText(
      l10n_util::GetStringUTF16(IDS_ASSISTANT_BETTER_ONBOARDING_INTRO));

  // Suggestions.
  UpdateSuggestions();
}

void AssistantOnboardingView::UpdateSuggestions() {
  if (table_)
    RemoveChildViewT(table_.get());

  table_ = AddChildView(std::make_unique<views::TableLayoutView>());
  table_->SetBorder(views::CreateEmptyBorder(
      gfx::Insets::TLBR(kSuggestionsMarginTopDip, 0, 0, 0)));

  // Initialize columns.
  for (int i = 0; i < kSuggestionsColumnCount; ++i) {
    if (i > 0) {
      table_->AddPaddingColumn(
          /*horizontal_resize=*/views::TableLayout::kFixedSize,
          /*width=*/kSuggestionsMarginDip);
    }
    table_->AddColumn(
        /*h_align=*/views::LayoutAlignment::kStretch,
        /*v_align=*/views::LayoutAlignment::kStretch, /*horizontal_resize=*/1.0,
        /*size_type=*/views::TableLayout::ColumnSize::kFixed,
        /*fixed_width=*/0, /*min_width=*/0);
  }

  const std::vector<AssistantSuggestion>& suggestions =
      AssistantSuggestionsController::Get()
          ->GetModel()
          ->GetOnboardingSuggestions();

  // Initialize suggestions.
  for (size_t i = 0; i < suggestions.size() && i < kSuggestionsMaxCount; ++i) {
    if (i % kSuggestionsColumnCount == 0) {
      if (i > 0) {
        table_->AddPaddingRow(
            /*vertical_resize=*/views::TableLayout::kFixedSize,
            /*height=*/kSuggestionsMarginDip);
      }
      table_->AddRows(/*n=*/1,
                      /*vertical_resize=*/views::TableLayout::kFixedSize);
    }
    table_->AddChildView(std::make_unique<AssistantOnboardingSuggestionView>(
        delegate_, suggestions.at(i), i));
  }
}

void AssistantOnboardingView::UpdateGreeting() {
  greeting_->SetText(base::UTF8ToUTF16(GetGreetingMessage(delegate_)));
}

BEGIN_METADATA(AssistantOnboardingView)
END_METADATA

}  // namespace ash