text:
Austria's Eurovision 2017 Entry
Nathan Trent will represent Austria at this year's Eurovision Song Contest with "Running On Air."
Trent is a 24-year-old pop singer from Innsbruck, Austria, so we're already inclined to like him. He was on the shortlist of this year's the German national final, but was disqualified when he became Austria's internal selection. He co-wrote "Running On Air" with Austrian producer and songwriter Bernhard Penzias.
It's always hard to judge a Eurovision contestant based on a music video instead of a live performance. All we can tell really is that despite being from Innsbruck, he sucks at mountaineering.
We like the song. It's upbeat, pleasant, and fun in a Maroon 5 kind of way. But we need more information. Judging from his bio, Trent is very early in his music career. He was willing to jump into Unser Song while also negotiating to represent Austria, so he certainly seems to think he's ready. But we have no idea if anything has prepared him for this. Many a young artist before has crumbled under the intensity and the chaos of Eurovision. Let's hope that he can keep it together in Kyiv this May.
Author Chris ZammarelliPosted on 1 March 2017 7 March 2020 Categories ESC2017Tags Austria
Previous Previous post: Moldova's Eurovision 2017 Entry
Next Next post: Cyprus' Eurovision 2017 Entry


text:
// Copyright 2021 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/system/message_center/ash_notification_view.h"

#include <memory>
#include <utility>

#include "ash/public/cpp/metrics_util.h"
#include "ash/public/cpp/rounded_image_view.h"
#include "ash/public/cpp/style/color_provider.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/dark_light_mode_controller_impl.h"
#include "ash/style/icon_button.h"
#include "ash/style/pill_button.h"
#include "ash/system/message_center/ash_notification_expand_button.h"
#include "ash/system/message_center/ash_notification_input_container.h"
#include "ash/system/message_center/message_center_constants.h"
#include "ash/system/message_center/message_center_style.h"
#include "ash/system/message_center/message_center_utils.h"
#include "ash/system/message_center/metrics_utils.h"
#include "ash/system/message_center/notification_grouping_controller.h"
#include "ash/wm/work_area_insets.h"
#include "base/bind.h"
#include "base/check.h"
#include "base/metrics/histogram_functions.h"
#include "base/ranges/algorithm.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_id.h"
#include "ui/color/color_provider.h"
#include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/font.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/text_constants.h"
#include "ui/gfx/text_elider.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification_view_controller.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/vector_icons.h"
#include "ui/message_center/views/notification_background_painter.h"
#include "ui/message_center/views/notification_control_buttons_view.h"
#include "ui/message_center/views/notification_header_view.h"
#include "ui/message_center/views/notification_view_base.h"
#include "ui/message_center/views/proportional_image_view.h"
#include "ui/message_center/views/relative_time_formatter.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/animation/animation_builder.h"
#include "ui/views/background.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/layout/table_layout.h"
#include "ui/views/metadata/view_factory_internal.h"
#include "ui/views/style/typography.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"

namespace {

constexpr auto kNotificationViewPadding = gfx::Insets(4);
constexpr int kMainRightViewVerticalSpacing = 4;

// This padding is applied to all the children of `main_right_view_` except the
// action buttons.
constexpr auto kMainRightViewChildPadding = gfx::Insets::TLBR(0, 14, 0, 0);

constexpr auto kImageContainerPadding = gfx::Insets::TLBR(12, 14, 12, 12);

constexpr auto kActionButtonsRowPadding = gfx::Insets::TLBR(4, 38, 12, 4);
constexpr int kActionsRowHorizontalSpacing = 8;

constexpr auto kContentRowPadding = gfx::Insets::TLBR(16, 0, 0, 0);

constexpr int kLeftContentVerticalSpacing = 4;
constexpr int kTitleRowMinimumWidth = 186;
constexpr int kTitleRowSpacing = 6;

constexpr auto kHeaderRowExpandedPadding = gfx::Insets::TLBR(4, 0, 8, 0);
constexpr auto kHeaderRowCollapsedPadding = gfx::Insets::TLBR(0, 0, 8, 0);
constexpr auto kRightContentCollapsedPadding = gfx::Insets::TLBR(12, 0, 0, 16);
constexpr auto kRightContentExpandedPadding = gfx::Insets::TLBR(20, 0, 0, 16);
constexpr auto kTimeStampInCollapsedStatePadding =
    gfx::Insets::TLBR(0, 0, 0, 16);

// Bullet character. The divider symbol between the title and the timestamp.
constexpr char16_t kTitleRowDivider[] = u"\u2022";

constexpr char kGoogleSansFont[] = "Google Sans";

constexpr int kAppIconViewSize = 24;
constexpr int kAppIconImageSize = 16;
constexpr int kTitleCharacterLimit =
    message_center::kNotificationWidth * message_center::kMaxTitleLines /
    message_center::kMinPixelsPerTitleCharacter;
constexpr int kTitleLabelSize = 13;
constexpr int kTitleLabelExpandedMaxLines = 2;
constexpr int kTitleLabelCollapsedMaxLines = 1;
constexpr int kTimestampInCollapsedViewSize = 12;
constexpr int kMessageLabelSize = 12;
// The size for `icon_view_`, which is the icon within right content (between
// title/message view and expand button).
constexpr int kIconViewSize = 48;

// Target contrast ratio to reach when adjusting colors in dark mode.
constexpr float kDarkModeMinContrastRatio = 6.0;

// If the image displayed in `icon_view()` is smaller in either width or height
// than this value, we draw a background around the image.
constexpr int kSmallImageBackgroundThreshold = 6;

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

// Configure the style for labels in notification view. `is_color_primary`
// indicates if the color of the text is primary or secondary text color.
void ConfigureLabelStyle(
    views::Label* label,
    int size,
    bool is_color_primary,
    gfx::Font::Weight font_weight = gfx::Font::Weight::NORMAL) {
  label->SetAutoColorReadabilityEnabled(false);
  label->SetFontList(
      gfx::FontList({kGoogleSansFont}, gfx::Font::NORMAL, size, font_weight));
  auto layer_type =
      is_color_primary
          ? ash::AshColorProvider::ContentLayerType::kTextColorPrimary
          : ash::AshColorProvider::ContentLayerType::kTextColorSecondary;
  label->SetEnabledColor(
      ash::AshColorProvider::Get()->GetContentLayerColor(layer_type));
}

// Create a view that will contain the `content_row`,
// `message_label_in_expanded_state_`, inline settings and the large image.
views::Builder<views::View> CreateMainRightViewBuilder() {
  auto layout_manager = std::make_unique<views::FlexLayout>();
  layout_manager
      ->SetDefault(views::kMarginsKey,
                   gfx::Insets::TLBR(0, 0, kMainRightViewVerticalSpacing, 0))
      .SetOrientation(views::LayoutOrientation::kVertical);

  return views::Builder<views::View>()
      .SetID(message_center::NotificationViewBase::ViewId::kMainRightView)
      .SetLayoutManager(std::move(layout_manager))
      .SetProperty(
          views::kFlexBehaviorKey,
          views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
                                   views::MaximumFlexSizeRule::kUnbounded));
}

// Create a view containing the title and message for the notification in a
// single line. This is used when a grouped child notification is in a
// collapsed parent notification.
views::Builder<views::BoxLayoutView> CreateCollapsedSummaryBuilder(
    const message_center::Notification& notification) {
  return views::Builder<views::BoxLayoutView>()
      .SetID(
          message_center::NotificationViewBase::ViewId::kCollapsedSummaryView)
      .SetInsideBorderInsets(ash::kGroupedCollapsedSummaryInsets)
      .SetBetweenChildSpacing(ash::kGroupedCollapsedSummaryLabelSpacing)
      .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
      .SetVisible(false)
      .AddChild(views::Builder<views::Label>()
                    .SetText(notification.title())
                    .SetFontList(gfx::FontList(
                        {kGoogleSansFont}, gfx::Font::NORMAL, kTitleLabelSize,
                        gfx::Font::Weight::MEDIUM)))
      .AddChild(views::Builder<views::Label>()
                    .SetText(notification.message())
                    .SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT)
                    .SetTextStyle(views::style::STYLE_SECONDARY)
                    .SetFontList(gfx::FontList(
                        {kGoogleSansFont}, gfx::Font::NORMAL, kMessageLabelSize,
                        gfx::Font::Weight::MEDIUM)));
}

views::Builder<ash::AshNotificationView::GroupedNotificationsContainer>
CreateGroupedNotificationsContainerBuilder(
    ash::AshNotificationView* parent_notification_view) {
  return views::Builder<
             ash::AshNotificationView::GroupedNotificationsContainer>()
      .SetParentNotificationView(parent_notification_view)
      .SetOrientation(views::BoxLayout::Orientation::kVertical);
}

// Perform a scale and translate animation by scale from (scale_value_x,
// scalue_value_y) and translate from (translate_value_x, translate_value_y) to
// its correct scale and position.
void ScaleAndTranslateView(views::View* view,
                           SkScalar scale_value_x,
                           SkScalar scale_value_y,
                           SkScalar translate_value_x,
                           SkScalar translate_value_y,
                           const std::string& animation_histogram_name) {
  gfx::Transform transform;
  transform.Translate(translate_value_x, translate_value_y);
  transform.Scale(scale_value_x, scale_value_y);

  ui::AnimationThroughputReporter reporter(
      view->layer()->GetAnimator(),
      ash::metrics_util::ForSmoothness(base::BindRepeating(
          [](const std::string& animation_histogram_name, int smoothness) {
            base::UmaHistogramPercentage(animation_histogram_name, smoothness);
          },
          animation_histogram_name)));

  views::AnimationBuilder()
      .SetPreemptionStrategy(
          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
      .Once()
      .SetDuration(base::TimeDelta())
      .SetTransform(view, transform)
      .Then()
      .SetDuration(
          base::Milliseconds(ash::kLargeImageScaleAndTranslateDurationMs))
      .SetTransform(view, gfx::Transform(), gfx::Tween::ACCEL_0_100_DECEL_80);
}

}  // namespace

namespace ash {

using CrossAxisAlignment = views::BoxLayout::CrossAxisAlignment;
using MainAxisAlignment = views::BoxLayout::MainAxisAlignment;
using Orientation = views::BoxLayout::Orientation;

BEGIN_METADATA(AshNotificationView, NotificationTitleRow, views::View)
END_METADATA

void AshNotificationView::AddedToWidget() {
  // crbug/1337661: We need to abort animations in a grouped parent view when
  // it's widget is being destroyed. By default when a widget is destroyed, all
  // current animations are forced to finish. The grouped notification removal
  // animation triggers an additional resize animation when it is finished. This
  // needs to be aborted explicitly to prevent a crash. We do not need to this
  // observation for grouped notification views.
  if (!is_grouped_child_view_)
    widget_observation_.Observe(GetWidget());
}

void AshNotificationView::Layout() {
  if (is_animating_)
    return;

  message_center::NotificationViewBase::Layout();
}

void AshNotificationView::GroupedNotificationsContainer::
    ChildPreferredSizeChanged(views::View* view) {
  PreferredSizeChanged();
  parent_notification_view_->GroupedNotificationsPreferredSizeChanged();
}

void AshNotificationView::GroupedNotificationsContainer::
    SetParentNotificationView(AshNotificationView* parent_notification_view) {
  parent_notification_view_ = parent_notification_view;
}

AshNotificationView::NotificationTitleRow::NotificationTitleRow(
    const std::u16string& title)
    : title_view_(AddChildView(GenerateTitleView(title))),
      title_row_divider_(AddChildView(std::make_unique<views::Label>(
          kTitleRowDivider,
          views::style::CONTEXT_DIALOG_BODY_TEXT))),
      timestamp_in_collapsed_view_(
          AddChildView(std::make_unique<views::Label>())) {
  SetLayoutManager(std::make_unique<views::TableLayout>())
      ->AddColumn(views::LayoutAlignment::kStart,
                  views::LayoutAlignment::kCenter, 1.0,
                  views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
      .AddPaddingColumn(views::TableLayout::kFixedSize, kTitleRowSpacing)
      .AddColumn(views::LayoutAlignment::kStart,
                 views::LayoutAlignment::kCenter,
                 views::TableLayout::kFixedSize,
                 views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
      .AddPaddingColumn(views::TableLayout::kFixedSize, kTitleRowSpacing)
      .AddColumn(views::LayoutAlignment::kStart,
                 views::LayoutAlignment::kCenter, 100.0,
                 views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
      .AddRows(1, views::TableLayout::kFixedSize);

  timestamp_in_collapsed_view_->SetProperty(views::kMarginsKey,
                                            kTimeStampInCollapsedStatePadding);
  timestamp_in_collapsed_view_->SetElideBehavior(gfx::ElideBehavior::NO_ELIDE);
  title_view_->SetProperty(
      views::kFlexBehaviorKey,
      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
                               views::MaximumFlexSizeRule::kPreferred));
  title_view_->SetMultiLine(true);
  title_view_->SetAllowCharacterBreak(true);
  title_view_->SetMaxLines(kTitleLabelExpandedMaxLines);

  ConfigureLabelStyle(title_row_divider_, kTimestampInCollapsedViewSize,
                      /*is_color_primary=*/false);
  message_center_utils::InitLayerForAnimations(title_row_divider_);
  ConfigureLabelStyle(timestamp_in_collapsed_view_,
                      kTimestampInCollapsedViewSize,
                      /*is_color_primary=*/false);
  message_center_utils::InitLayerForAnimations(timestamp_in_collapsed_view_);
  ConfigureLabelStyle(title_view_, kTitleLabelSize,
                      /*is_color_primary=*/true, gfx::Font::Weight::MEDIUM);
}

AshNotificationView::NotificationTitleRow::~NotificationTitleRow() {
  timestamp_update_timer_.Stop();
}

void AshNotificationView::NotificationTitleRow::UpdateTitle(
    const std::u16string& title) {
  title_view_->SetText(title);
}

void AshNotificationView::NotificationTitleRow::UpdateTimestamp(
    base::Time timestamp) {
  std::u16string relative_time;
  base::TimeDelta next_update;
  message_center::GetRelativeTimeStringAndNextUpdateTime(
      timestamp - base::Time::Now(), &relative_time, &next_update);

  timestamp_ = timestamp;
  timestamp_in_collapsed_view_->SetText(relative_time);

  // Unretained is safe as the timer cancels the task on destruction.
  timestamp_update_timer_.Start(
      FROM_HERE, next_update,
      base::BindOnce(&NotificationTitleRow::UpdateTimestamp,
                     base::Unretained(this), timestamp));
}

void AshNotificationView::NotificationTitleRow::UpdateVisibility(
    bool in_collapsed_mode) {
  timestamp_in_collapsed_view_->SetVisible(in_collapsed_mode);
  title_row_divider_->SetVisible(in_collapsed_mode);
}

void AshNotificationView::NotificationTitleRow::
    PerformExpandCollapseAnimation() {
  if (title_row_divider_->GetVisible()) {
    message_center_utils::FadeInView(
        title_row_divider_, kTitleRowTimestampFadeInAnimationDelayMs,
        kTitleRowTimestampFadeInAnimationDurationMs,
        gfx::Tween::ACCEL_20_DECEL_100,
        "Ash.NotificationView.TitleRowDivider.FadeIn.AnimationSmoothness");
    DCHECK(timestamp_in_collapsed_view_->GetVisible());
    message_center_utils::FadeInView(
        timestamp_in_collapsed_view_, kTitleRowTimestampFadeInAnimationDelayMs,
        kTitleRowTimestampFadeInAnimationDurationMs,
        gfx::Tween::ACCEL_20_DECEL_100,
        "Ash.NotificationView.TimestampInTitle.FadeIn.AnimationSmoothness");
  }
}

gfx::Size AshNotificationView::NotificationTitleRow::CalculatePreferredSize()
    const {
  // TODO(crbug.com/1349528): The size constraint is not passed down from the
  // views tree in the first round of layout, so setting a fixed width to bound
  // the view. The layout manager can size the view beyond this width if there
  // is available space. This works similar to applying a max width on the
  // internal labels.
  return gfx::Size(kTitleRowMinimumWidth,
                   GetHeightForWidth(kTitleRowMinimumWidth));
}

void AshNotificationView::NotificationTitleRow::OnThemeChanged() {
  views::View::OnThemeChanged();

  title_view_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
      AshColorProvider::ContentLayerType::kTextColorPrimary));

  SkColor secondary_text_color = AshColorProvider::Get()->GetContentLayerColor(
      AshColorProvider::ContentLayerType::kTextColorSecondary);
  title_row_divider_->SetEnabledColor(secondary_text_color);
  timestamp_in_collapsed_view_->SetEnabledColor(secondary_text_color);
}

// static
const char AshNotificationView::kViewClassName[] = "AshNotificationView";

AshNotificationView::AshNotificationView(
    const message_center::Notification& notification,
    bool shown_in_popup)
    : NotificationViewBase(notification),
      is_grouped_parent_view_(notification.group_parent()),
      is_grouped_child_view_(notification.group_child()),
      shown_in_popup_(shown_in_popup) {
  message_center_observer_.Observe(message_center::MessageCenter::Get());
  // TODO(crbug/1232197): fix views and layout to match spec.
  // Instantiate view instances and define layout and view hierarchy.
  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical,
      notification.group_child() ? gfx::Insets() : kNotificationViewPadding));

  auto content_row_layout = std::make_unique<views::FlexLayout>();
  content_row_layout->SetInteriorMargin(kMainRightViewChildPadding);

  auto content_row_builder =
      CreateContentRowBuilder()
          .SetLayoutManager(std::move(content_row_layout))
          .AddChild(
              views::Builder<views::FlexLayoutView>()
                  .SetID(kHeaderLeftContent)
                  .SetOrientation(views::LayoutOrientation::kVertical)
                  .SetInteriorMargin(kContentRowPadding)
                  .SetProperty(views::kFlexBehaviorKey,
                               views::FlexSpecification(
                                   views::MinimumFlexSizeRule::kScaleToZero,
                                   views::MaximumFlexSizeRule::kScaleToMaximum))
                  .AddChild(
                      CreateHeaderRowBuilder()
                          .SetIsInAshNotificationView(true)
                          .SetColor(
                              AshColorProvider::Get()->GetContentLayerColor(
                                  AshColorProvider::ContentLayerType::
                                      kTextColorSecondary)))
                  .AddChild(
                      CreateLeftContentBuilder()
                          .CopyAddressTo(&left_content_)
                          .SetBetweenChildSpacing(kLeftContentVerticalSpacing)))
          .AddChild(
              views::Builder<views::FlexLayoutView>()
                  .SetMainAxisAlignment(views::LayoutAlignment::kEnd)
                  .SetProperty(views::kFlexBehaviorKey,
                               views::FlexSpecification(
                                   views::MinimumFlexSizeRule::kPreferred,
                                   views::MaximumFlexSizeRule::kUnbounded))
                  .AddChild(CreateRightContentBuilder().SetProperty(
                      views::kCrossAxisAlignmentKey,
                      views::LayoutAlignment::kStart))
                  .AddChild(
                      views::Builder<views::FlexLayoutView>()
                          .SetOrientation(views::LayoutOrientation::kVertical)
                          .AddChild(
                              views::Builder<views::BoxLayoutView>()
                                  .SetMainAxisAlignment(MainAxisAlignment::kEnd)
                                  .SetMinimumCrossAxisSize(
                                      kControlButtonsContainerMinimumHeight)
                                  .AddChild(
                                      CreateControlButtonsBuilder()
                                          .CopyAddressTo(&control_buttons_view_)
                                          .SetProperty(
                                              views::kCrossAxisAlignmentKey,
                                              views::LayoutAlignment::kEnd)
                                          .SetButtonIconColors(
                                              AshColorProvider::Get()
                                                  ->GetContentLayerColor(
                                                      AshColorProvider::
                                                          ContentLayerType::
                                                              kIconColorPrimary))))
                          .AddChild(
                              views::Builder<AshNotificationExpandButton>()
                                  .CopyAddressTo(&expand_button_)
                                  .SetCallback(base::BindRepeating(
                                      &AshNotificationView::ToggleExpand,
                                      base::Unretained(this)))
                                  .SetProperty(views::kCrossAxisAlignmentKey,
                                               views::LayoutAlignment::kEnd))));

  // Main right view contains all the views besides control buttons, app icon,
  // grouped container and action buttons.
  auto main_right_view_builder =
      CreateMainRightViewBuilder()
          .CopyAddressTo(&main_right_view_)
          .AddChild(content_row_builder)
          .AddChild(
              views::Builder<views::Label>()
                  .CopyAddressTo(&message_label_in_expanded_state_)
                  .SetHorizontalAlignment(gfx::ALIGN_TO_HEAD)
                  .SetMultiLine(true)
                  .SetMaxLines(message_center::kMaxLinesForExpandedMessageLabel)
                  .SetAllowCharacterBreak(true)
                  .SetBorder(
                      views::CreateEmptyBorder(kMainRightViewChildPadding))
                  // TODO(crbug/682266): This is a workaround to that bug by
                  // explicitly setting the width. Ideally, we should fix the
                  // original bug, but it seems there's no obvious solution for
                  // the bug according to https://crbug.com/678337#c7. We will
                  // consider making changes to this code when the bug is fixed.
                  .SetMaximumWidth(GetExpandedMessageLabelWidth()))
          .AddChild(CreateInlineSettingsBuilder())
          .AddChild(CreateImageContainerBuilder().SetProperty(
              views::kMarginsKey, kImageContainerPadding));

  ConfigureLabelStyle(message_label_in_expanded_state_, kMessageLabelSize,
                      false);

  AddChildView(
      views::Builder<views::FlexLayoutView>()
          .CopyAddressTo(&main_view_)
          .SetOrientation(views::LayoutOrientation::kHorizontal)
          .AddChild(views::Builder<views::BoxLayoutView>()
                        .SetID(kAppIconViewContainer)
                        .SetOrientation(Orientation::kVertical)
                        .SetMainAxisAlignment(MainAxisAlignment::kStart)
                        .AddChild(views::Builder<RoundedImageView>()
                                      .CopyAddressTo(&app_icon_view_)
                                      .SetCornerRadius(kAppIconViewSize / 2)))
          .AddChild(main_right_view_builder)
          .Build());

  AddChildView(CreateCollapsedSummaryBuilder(notification)
                   .CopyAddressTo(&collapsed_summary_view_)
                   .Build());

  // We only need a scroll view if the notification is being shown in its own
  // popup. Scrolling is handled by `UnifiedMessageCenterView` otherwise. Having
  // a nested scroll view results in crbug/1302756.
  if (shown_in_popup) {
    AddChildView(
        views::Builder<views::ScrollView>()
            .CopyAddressTo(&grouped_notifications_scroll_view_)
            .SetBackgroundColor(absl::nullopt)
            .SetDrawOverflowIndicator(false)
            .ClipHeightTo(0, std::numeric_limits<int>::max())
            .SetContents(
                CreateGroupedNotificationsContainerBuilder(this).CopyAddressTo(
                    &grouped_notifications_container_))
            .Build());
    static_cast<views::BoxLayout*>(GetLayoutManager())
        ->SetFlexForView(grouped_notifications_scroll_view_, 1);
  } else {
    AddChildView(CreateGroupedNotificationsContainerBuilder(this)
                     .CopyAddressTo(&grouped_notifications_container_)
                     .Build());
  }

  AddChildView(CreateActionsRow(std::make_unique<views::FlexLayout>()));

  // Custom layout and paddings for views in `AshNotificationView`.
  // Note that we also change the padding for some particular views in
  // UpdateViewForExpandedState().
  action_buttons_row()
      ->SetLayoutManager(std::make_unique<views::FlexLayout>())
      ->SetDefault(views::kMarginsKey,
                   gfx::Insets::TLBR(0, 0, 0, kActionsRowHorizontalSpacing))
      .SetOrientation(views::LayoutOrientation::kHorizontal)
      .SetInteriorMargin(kActionButtonsRowPadding);
  action_buttons_row()->SetProperty(
      views::kFlexBehaviorKey,
      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
                               views::MaximumFlexSizeRule::kUnbounded));
  inline_reply()->SetProperty(
      views::kFlexBehaviorKey,
      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
                               views::MaximumFlexSizeRule::kUnbounded));

  static_cast<views::FlexLayout*>(header_row()->GetLayoutManager())
      ->SetDefault(views::kMarginsKey, gfx::Insets())
      .SetInteriorMargin(gfx::Insets());
  header_row()->ConfigureLabelsStyle(
      gfx::FontList({kGoogleSansFont}, gfx::Font::NORMAL, kHeaderViewLabelSize,
                    gfx::Font::Weight::NORMAL),
      gfx::Insets(), true);

  // Corner radius for popups is handled below. We do not set corner radius if
  // the view is  in the message center here. Rounded corners for message_views
  // in the message center view are handled in `UnifiedMessageListView`.
  if (shown_in_popup_ && !notification.group_child()) {
    UpdateCornerRadius(kMessagePopupCornerRadius, kMessagePopupCornerRadius);

    layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
    layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
    layer()->SetRoundedCornerRadius(
        gfx::RoundedCornersF{kMessagePopupCornerRadius});
    layer()->SetIsFastRoundedCorner(true);
  }

  views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);

  // Create layer in some views for animations.
  message_center_utils::InitLayerForAnimations(header_row());
  message_center_utils::InitLayerForAnimations(
      message_label_in_expanded_state_);
  message_center_utils::InitLayerForAnimations(actions_row());

  UpdateWithNotification(notification);
}

AshNotificationView::~AshNotificationView() = default;

void AshNotificationView::SetGroupedChildExpanded(bool expanded) {
  collapsed_summary_view_->SetVisible(!expanded);
  main_view_->SetVisible(expanded);
}

void AshNotificationView::AnimateGroupedChildExpandedCollapse(bool expanded) {
  message_center_utils::InitLayerForAnimations(collapsed_summary_view_);
  message_center_utils::InitLayerForAnimations(main_view_);
  // Fade out `collapsed_summary_view_`, then fade in `main_view_` in expanded
  // state and vice versa in collapsed state.
  if (expanded) {
    message_center_utils::FadeOutView(
        collapsed_summary_view_,
        base::BindRepeating(
            [](base::WeakPtr<ash::AshNotificationView> parent,
               views::View* collapsed_summary_view) {
              if (parent) {
                collapsed_summary_view->layer()->SetOpacity(1.0f);
                collapsed_summary_view->SetVisible(false);
              }
            },
            weak_factory_.GetWeakPtr(), collapsed_summary_view_),
        0, kCollapsedSummaryViewAnimationDurationMs, gfx::Tween::LINEAR,
        "Ash.NotificationView.CollapsedSummaryView.FadeOut."
        "AnimationSmoothness");
    message_center_utils::FadeInView(
        main_view_, kCollapsedSummaryViewAnimationDurationMs,
        kChildMainViewFadeInAnimationDurationMs, gfx::Tween::LINEAR,
        "Ash.NotificationView.MainView.FadeIn.AnimationSmoothness");
    return;
  }

  message_center_utils::FadeOutView(
      main_view_,
      base::BindRepeating(
          [](base::WeakPtr<ash::AshNotificationView> parent,
             views::View* main_view) {
            if (parent) {
              main_view->layer()->SetOpacity(1.0f);
              main_view->SetVisible(false);
            }
          },
          weak_factory_.GetWeakPtr(), main_view_),
      0, kChildMainViewFadeOutAnimationDurationMs, gfx::Tween::LINEAR,
      "Ash.NotificationView.MainView.FadeOut.AnimationSmoothness");
  message_center_utils::FadeInView(
      collapsed_summary_view_, kChildMainViewFadeOutAnimationDurationMs,
      kCollapsedSummaryViewAnimationDurationMs, gfx::Tween::LINEAR,
      "Ash.NotificationView.CollapsedSummaryView.FadeIn.AnimationSmoothness");
}

void AshNotificationView::AnimateSingleToGroup(
    NotificationGroupingController* grouping_controller,
    const std::string& notification_id,
    std::string parent_id) {
  ash::message_center_utils::InitLayerForAnimations(left_content());
  ash::message_center_utils::InitLayerForAnimations(right_content());
  ash::message_center_utils::InitLayerForAnimations(
      message_label_in_expanded_state_);
  ash::message_center_utils::InitLayerForAnimations(image_container_view());
  ash::message_center_utils::InitLayerForAnimations(action_buttons_row());

  auto on_animation_ended = base::BindOnce(
      [](base::WeakPtr<ash::AshNotificationView> parent,
         views::View* left_content, views::View* right_content,
         views::View* message_label_in_expanded_state,
         views::View* image_container_view, views::View* action_buttons_row,
         AshNotificationExpandButton* expand_button,
         NotificationGroupingController* grouping_controller,
         const std::string& notification_id, std::string parent_id) {
        if (!parent)
          return;

        auto* parent_notification =
            message_center::MessageCenter::Get()->FindNotificationById(
                parent_id);
        auto* child_notification =
            message_center::MessageCenter::Get()->FindNotificationById(
                notification_id);
        // The child and parent notifications are not guaranteed to exist. If
        // they were deleted avoid the animation cleanup.
        if (!parent_notification || !child_notification)
          return;

        grouping_controller->ConvertFromSingleToGroupNotificationAfterAnimation(
            notification_id, parent_id, parent_notification);

        left_content->layer()->SetOpacity(1.0f);
        right_content->layer()->SetOpacity(1.0f);
        message_label_in_expanded_state->layer()->SetOpacity(1.0f);
        image_container_view->layer()->SetOpacity(1.0f);
        action_buttons_row->layer()->SetOpacity(1.0f);

        // After fade out single notification and set up a group one, perform
        // a fade in.
        parent->AnimateSingleToGroupFadeIn();

        expand_button->set_previous_bounds(expand_button->GetContentsBounds());
        parent->Layout();
        expand_button->AnimateSingleToGroupNotification();
      },
      weak_factory_.GetWeakPtr(), left_content_, right_content(),
      message_label_in_expanded_state_, image_container_view(),
      action_buttons_row(), expand_button_, grouping_controller,
      notification_id, parent_id);

  std::pair<base::OnceClosure, base::OnceClosure> split =
      base::SplitOnceCallback(std::move(on_animation_ended));

  ui::AnimationThroughputReporter reporter(
      left_content()->layer()->GetAnimator(),
      metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) {
        base::UmaHistogramPercentage(
            "Ash.NotificationView.ConvertSingleToGroup.FadeOut."
            "AnimationSmoothness",
            smoothness);
      })));

  views::AnimationBuilder()
      .SetPreemptionStrategy(
          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
      .OnEnded(std::move(split.first))
      .OnAborted(std::move(split.second))
      .Once()
      .SetDuration(
          base::Milliseconds(kConvertFromSingleToGroupFadeOutDurationMs))
      .SetOpacity(left_content(), 0.0f, gfx::Tween::LINEAR)
      .SetOpacity(right_content(), 0.0f, gfx::Tween::LINEAR)
      .SetOpacity(message_label_in_expanded_state_, 0.0f, gfx::Tween::LINEAR)
      .SetOpacity(image_container_view(), 0.0f, gfx::Tween::LINEAR)
      .SetOpacity(action_buttons_row(), 0.0f, gfx::Tween::LINEAR);
}

void AshNotificationView::ToggleExpand() {
  SetManuallyExpandedOrCollapsed(true);

  if (inline_reply() && inline_reply()->GetVisible()) {
    message_center_utils::FadeOutView(
        inline_reply(),
        base::BindOnce(
            [](base::WeakPtr<ash::AshNotificationView> parent,
               views::View* inline_reply) {
              if (parent) {
                inline_reply->layer()->SetOpacity(1.0f);
              }
            },
            weak_factory_.GetWeakPtr(), inline_reply()),
        /*delay_in_ms=*/0, kInlineReplyFadeOutAnimationDurationMs,
        gfx::Tween::LINEAR,
        "Ash.NotificationView.InlineReply.FadeOut.AnimationSmoothness");
  }

  SetExpanded(!IsExpanded());

  PerformExpandCollapseAnimation();

  // Log expand button click action.
  if (IsExpanded()) {
    is_grouped_parent_view_
        ? metrics_utils::LogExpandButtonClickAction(
              metrics_utils::ExpandButtonClickAction::EXPAND_GROUP)
        : metrics_utils::LogExpandButtonClickAction(
              metrics_utils::ExpandButtonClickAction::EXPAND_INDIVIDUAL);
  } else {
    is_grouped_parent_view_
        ? metrics_utils::LogExpandButtonClickAction(
              metrics_utils::ExpandButtonClickAction::COLLAPSE_GROUP)
        : metrics_utils::LogExpandButtonClickAction(
              metrics_utils::ExpandButtonClickAction::COLLAPSE_INDIVIDUAL);
  }
}

void AshNotificationView::GroupedNotificationsPreferredSizeChanged() {
  PreferredSizeChanged();
}

base::TimeDelta AshNotificationView::GetBoundsAnimationDuration(
    const message_center::Notification& notification) const {
  // This is called after the parent gets notified of
  // `ChildPreferredSizeChanged()`, so the current expanded state is the target
  // state.
  if (!notification.image().IsEmpty())
    return base::Milliseconds(kLargeImageExpandAndCollapseAnimationDuration);

  if (HasInlineReply(notification) || is_grouped_parent_view_) {
    if (IsExpanded()) {
      return base::Milliseconds(
          kInlineReplyAndGroupedParentExpandAnimationDuration);
    }
    return base::Milliseconds(
        kInlineReplyAndGroupedParentCollapseAnimationDuration);
  }

  if (inline_settings_row() && inline_settings_row()->GetVisible()) {
    return base::Milliseconds(
        kInlineSettingsExpandAndCollapseAnimationDuration);
  }

  if (IsExpanded())
    return base::Milliseconds(kGeneralExpandAnimationDuration);
  return base::Milliseconds(kGeneralCollapseAnimationDuration);
}

void AshNotificationView::AddGroupNotification(
    const message_center::Notification& notification) {
  DCHECK(is_grouped_parent_view_);
  // Do not add a grouped notification if a view for it already exists.
  if (FindGroupNotificationView(notification.id()))
    return;

  auto notification_view =
      std::make_unique<AshNotificationView>(notification,
                                            /*shown_in_popup=*/false);
  notification_view->SetGroupedChildExpanded(IsExpanded());
  notification_view->set_parent_message_view(this);
  notification_view->set_scroller(
      scroller() ? scroller() : grouped_notifications_scroll_view_);

  header_row()->SetTimestamp(notification.timestamp());

  grouped_notifications_container_->AddChildViewAt(std::move(notification_view),
                                                   0);

  total_grouped_notifications_++;
  left_content_->SetVisible(false);
  UpdateGroupedNotificationsVisibility();
  expand_button_->UpdateGroupedNotificationsCount(total_grouped_notifications_);
  PreferredSizeChanged();
}

void AshNotificationView::PopulateGroupNotifications(
    const std::vector<const message_center::Notification*>& notifications) {
  DCHECK(is_grouped_parent_view_);
  // Clear all grouped notifications since we will add all grouped notifications
  // from scratch.
  total_grouped_notifications_ = 0;
  grouped_notifications_container_->RemoveAllChildViews();

  for (auto* notification : notifications) {
    auto notification_view =
        std::make_unique<AshNotificationView>(*notification,
                                              /*shown_in_popup=*/false);

    if (!total_grouped_notifications_)
      header_row()->SetTimestamp(notification->timestamp());

    notification_view->SetVisible(
        total_grouped_notifications_ <
            message_center_style::kMaxGroupedNotificationsInCollapsedState ||
        IsExpanded());
    notification_view->SetGroupedChildExpanded(IsExpanded());
    notification_view->set_parent_message_view(this);
    notification_view->set_scroller(
        scroller() ? scroller() : grouped_notifications_scroll_view_);

    grouped_notifications_container_->AddChildView(
        std::move(notification_view));

    total_grouped_notifications_++;
  }
  left_content_->SetVisible(total_grouped_notifications_ == 0);
  expand_button_->UpdateGroupedNotificationsCount(total_grouped_notifications_);
}

void AshNotificationView::RemoveGroupNotification(
    const std::string& notification_id) {
  auto* child_view = FindGroupNotificationView(notification_id);
  if (!child_view)
    return;

  base::WeakPtr<AshNotificationView> to_be_removed =
      static_cast<AshNotificationView*>(child_view)->weak_factory_.GetWeakPtr();
  if (to_be_removed) {
    // Abort any previously queued animations, if a remove animation was in
    // progress this will cause `to_be_removed` to be deleted. Because of this
    // we need to use a weakptr to ensure we do not try to animate an already
    // deleted view.
    to_be_removed->layer()->GetAnimator()->AbortAllAnimations();
  }

  if (!to_be_removed)
    return;

  auto on_notification_slid_out = base::BindRepeating(
      [](base::WeakPtr<AshNotificationView> self,
         const std::string& notification_id) {
        if (!self)
          return;

        views::View* to_be_removed =
            self->FindGroupNotificationView(notification_id);
        if (!to_be_removed)
          return;

        self->total_grouped_notifications_--;
        self->expand_button_->UpdateGroupedNotificationsCount(
            self->total_grouped_notifications_);

        self->AnimateResizeAfterRemoval(to_be_removed);
      },
      weak_factory_.GetWeakPtr(), notification_id);

  auto on_animation_aborted = base::BindRepeating(
      [](base::WeakPtr<AshNotificationView> self,
         const std::string& notification_id) {
        if (!self)
          return;

        views::View* to_be_removed =
            self->FindGroupNotificationView(notification_id);
        if (!to_be_removed)
          return;

        self->total_grouped_notifications_--;
        self->expand_button_->UpdateGroupedNotificationsCount(
            self->total_grouped_notifications_);

        self->grouped_notifications_container_->RemoveChildViewT(to_be_removed);
        self->PreferredSizeChanged();
      },
      weak_factory_.GetWeakPtr(), notification_id);

  // If the removed notification has a layer transform it has already been slid
  // out (For example user swiped it by dragging). We only need to animate a
  // slide out if there is no transform.
  if (to_be_removed && to_be_removed->layer()->transform().IsIdentity()) {
    message_center_utils::SlideOutView(
        to_be_removed.get(), on_notification_slid_out, on_animation_aborted,
        /*delay_in_ms=*/0,
        /*duration_in_ms=*/kSlideOutGroupedNotificationAnimationDurationMs,
        gfx::Tween::LINEAR,
        "Ash.Notification.GroupNotification.SlideOut.AnimationSmoothness");
  } else {
    on_notification_slid_out.Run();
  }
}

const char* AshNotificationView::GetClassName() const {
  return kViewClassName;
}

void AshNotificationView::UpdateViewForExpandedState(bool expanded) {
  // Grouped parent views should always use the expanded paddings, even if they
  // are collapsed.
  bool use_expanded_padding = expanded || is_grouped_parent_view_;

  header_row()->SetVisible(is_grouped_parent_view_ || expanded);
  header_row()->SetTimestampVisible(!is_grouped_parent_view_ || !expanded);

  if (title_row_) {
    title_row_->UpdateVisibility(IsExpandable() && !expanded);
    title_row_->title_view()->SetMaxLines(
        expanded ? kTitleLabelExpandedMaxLines : kTitleLabelCollapsedMaxLines);
  }

  if (message_label()) {
    // `message_label()` is shown only in collapsed mode.
    message_label()->SetVisible(!expanded);
    message_label_in_expanded_state_->SetVisible(expanded &&
                                                 !is_grouped_parent_view_);
  }

  // Custom padding for app icon and expand button. These 2 views should always
  // use the same padding value so that they are vertical aligned.
  app_icon_view_->SetProperty(views::kMarginsKey,
                              use_expanded_padding ? kAppIconExpandedPadding
                                                   : kAppIconCollapsedPadding);

  right_content()->SetProperty(
      views::kMarginsKey, use_expanded_padding ? kRightContentExpandedPadding
                                               : kRightContentCollapsedPadding);

  expand_button_->SetProperty(
      views::kMarginsKey, use_expanded_padding ? kExpandButtonExpandedPadding
                                               : kExpandButtonCollapsedPadding);
  header_row()->SetProperty(views::kMarginsKey,
                            use_expanded_padding ? kHeaderRowExpandedPadding
                                                 : kHeaderRowCollapsedPadding);

  expand_button_->SetExpanded(expanded);

  if (is_grouped_parent_view_) {
    if (grouped_notifications_scroll_view_) {
      grouped_notifications_scroll_view_->ClipHeightTo(
          0, CalculateMaxHeightForGroupedNotifications());
    }

    auto* grouped_notifications_container_layout_manager =
        static_cast<views::BoxLayout*>(
            grouped_notifications_container_->GetLayoutManager());
    grouped_notifications_container_layout_manager->set_inside_border_insets(
        expanded ? kGroupedNotificationContainerExpandedInsets
                 : kGroupedNotificationContainerCollapsedInsets);
    grouped_notifications_container_layout_manager->set_between_child_spacing(
        expanded ? kGroupedNotificationsExpandedSpacing
                 : kGroupedNotificationsCollapsedSpacing);

    int notification_count = 0;
    for (auto* child : grouped_notifications_container_->children()) {
      auto* notification_view = static_cast<AshNotificationView*>(child);
      notification_view->AnimateGroupedChildExpandedCollapse(expanded);
      notification_view->SetGroupedChildExpanded(expanded);
      notification_count++;
      if (notification_count >
          message_center_style::kMaxGroupedNotificationsInCollapsedState) {
        notification_view->SetVisible(expanded);
      }
    }
  }
  NotificationViewBase::UpdateViewForExpandedState(expanded);

  message_label_in_expanded_state_->SetProperty(
      views::kMarginsKey,
      (actions_row()->GetVisible() || image_container_view()->GetVisible() ||
               is_grouped_child_view_
           ? kMessageLabelInExpandedStatePadding
           : kMessageLabelInExpandedStateExtendedPadding));
}

void AshNotificationView::UpdateWithNotification(
    const message_center::Notification& notification) {
  is_grouped_child_view_ = notification.group_child();
  is_grouped_parent_view_ = notification.group_parent();

  if (grouped_notifications_scroll_view_)
    grouped_notifications_scroll_view_->SetVisible(is_grouped_parent_view_);
  grouped_notifications_container_->SetVisible(is_grouped_parent_view_);

  if (is_grouped_child_view_ && !is_nested())
    SetIsNested();

  header_row()->SetIsInGroupChildNotification(is_grouped_child_view_);
  UpdateMessageLabelInExpandedState(notification);

  NotificationViewBase::UpdateWithNotification(notification);

  CreateOrUpdateSnoozeButton(notification);

  // Configure views style.
  UpdateIconAndButtonsColor(&notification);
  if (message_label())
    ConfigureLabelStyle(message_label(), kMessageLabelSize, false);
  if (inline_reply()) {
    SkColor text_color = ash::AshColorProvider::Get()->GetContentLayerColor(
        ash::AshColorProvider::ContentLayerType::kTextColorSecondary);
    inline_reply()->textfield()->SetTextColor(text_color);
    inline_reply()->textfield()->set_placeholder_text_color(text_color);
  }
}

void AshNotificationView::CreateOrUpdateHeaderView(
    const message_center::Notification& notification) {
  switch (notification.system_notification_warning_level()) {
    case message_center::SystemNotificationWarningLevel::WARNING:
      header_row()->SetSummaryText(
          l10n_util::GetStringUTF16(IDS_ASH_NOTIFICATION_WARNING_LABEL));
      break;
    case message_center::SystemNotificationWarningLevel::CRITICAL_WARNING:
      header_row()->SetSummaryText(l10n_util::GetStringUTF16(
          IDS_ASH_NOTIFICATION_CRITICAL_WARNING_LABEL));
      break;
    case message_center::SystemNotificationWarningLevel::NORMAL:
      header_row()->SetSummaryText(std::u16string());
      break;
  }

  NotificationViewBase::CreateOrUpdateHeaderView(notification);
}

void AshNotificationView::CreateOrUpdateTitleView(
    const message_center::Notification& notification) {
  if (notification.title().empty()) {
    if (title_row_) {
      DCHECK(left_content()->Contains(title_row_));
      left_content()->RemoveChildViewT(title_row_);
      title_row_ = nullptr;
    }
    return;
  }

  const std::u16string& title = gfx::TruncateString(
      notification.title(), kTitleCharacterLimit, gfx::WORD_BREAK);

  if (!title_row_) {
    title_row_ =
        AddViewToLeftContent(std::make_unique<NotificationTitleRow>(title));
  } else {
    title_row_->UpdateTitle(title);
    ReorderViewInLeftContent(title_row_);
  }

  title_row_->UpdateTimestamp(notification.timestamp());
}

void AshNotificationView::CreateOrUpdateSmallIconView(
    const message_center::Notification& notification) {
  if (is_grouped_child_view_ && !notification.icon().IsEmpty()) {
    app_icon_view_->SetImage(notification.icon().Rasterize(GetColorProvider()),
                             gfx::Size(kAppIconViewSize, kAppIconViewSize));
    return;
  }

  UpdateAppIconView(&notification);
}

void AshNotificationView::CreateOrUpdateInlineSettingsViews(
    const message_center::Notification& notification) {
  if (inline_settings_enabled()) {
    DCHECK(message_center::SettingsButtonHandler::INLINE ==
           notification.rich_notification_data().settings_button_handler);
    return;
  }

  set_inline_settings_enabled(
      !is_grouped_child_view_ &&
      notification.rich_notification_data().settings_button_handler ==
          message_center::SettingsButtonHandler::INLINE);

  if (!inline_settings_enabled()) {
    return;
  }

  inline_settings_row()->SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kHorizontal));
  auto turn_off_notifications_button = GenerateNotificationLabelButton(
      base::BindRepeating(&AshNotificationView::DisableNotification,
                          base::Unretained(this)),
      l10n_util::GetStringUTF16(
          IDS_ASH_NOTIFICATION_INLINE_SETTINGS_TURN_OFF_BUTTON_TEXT));
  turn_off_notifications_button_ = inline_settings_row()->AddChildView(
      std::move(turn_off_notifications_button));
  auto inline_settings_cancel_button = GenerateNotificationLabelButton(
      base::BindRepeating(&AshNotificationView::ToggleInlineSettings,
                          base::Unretained(this)),
      l10n_util::GetStringUTF16(
          IDS_ASH_NOTIFICATION_INLINE_SETTINGS_CANCEL_BUTTON_TEXT));
  inline_settings_cancel_button_ = inline_settings_row()->AddChildView(
      std::move(inline_settings_cancel_button));
}

void AshNotificationView::CreateOrUpdateCompactTitleMessageView(
    const message_center::Notification& notification) {
  // No CompactTitleMessageView required. It is only used for progress
  // notifications when the notification is collapsed, and Ash progress
  // notifications only show a progress bar when collapsed.
}

void AshNotificationView::CreateOrUpdateProgressViews(
    const message_center::Notification& notification) {
  // AshNotificationView should have the status view, followed by the progress
  // bar. This is the opposite of what is required of the chrome notification.
  CreateOrUpdateProgressStatusView(notification);
  CreateOrUpdateProgressBarView(notification);
}

void AshNotificationView::UpdateControlButtonsVisibility() {
  NotificationViewBase::UpdateControlButtonsVisibility();

  // Always hide snooze button in control buttons since we show this snooze
  // button in actions button view.
  control_buttons_view()->ShowSnoozeButton(false);

  // Hide settings button for grouped child notifications.
  if (is_grouped_child_view_)
    control_buttons_view()->ShowSettingsButton(false);
}

bool AshNotificationView::IsIconViewShown() const {
  return NotificationViewBase::IsIconViewShown() && !is_grouped_child_view_;
}

void AshNotificationView::SetExpandButtonEnabled(bool enabled) {
  expand_button_->SetVisible(enabled);
}

bool AshNotificationView::IsExpandable() const {
  // Inline settings can not be expanded.
  if (GetMode() == Mode::SETTING)
    return false;

  // Notification should always be expandable since we hide `header_row()` in
  // collapsed state.
  return true;
}

void AshNotificationView::UpdateCornerRadius(int top_radius,
                                             int bottom_radius) {
  // Call parent's SetCornerRadius to update radius used for highlight path.
  NotificationViewBase::SetCornerRadius(top_radius, bottom_radius);
  UpdateBackground(top_radius, bottom_radius);
}

void AshNotificationView::SetDrawBackgroundAsActive(bool active) {}

void AshNotificationView::OnThemeChanged() {
  views::View::OnThemeChanged();
  UpdateBackground(top_radius_, bottom_radius_);

  SkColor secondary_text_color = AshColorProvider::Get()->GetContentLayerColor(
      AshColorProvider::ContentLayerType::kTextColorSecondary);
  header_row()->SetColor(secondary_text_color);

  if (message_label())
    message_label()->SetEnabledColor(secondary_text_color);

  if (control_buttons_view_) {
    control_buttons_view_->SetButtonIconColors(
        AshColorProvider::Get()->GetContentLayerColor(
            AshColorProvider::ContentLayerType::kIconColorPrimary));
  }

  if (message_label_in_expanded_state_)
    message_label_in_expanded_state_->SetEnabledColor(secondary_text_color);

  UpdateIconAndButtonsColor(
      message_center::MessageCenter::Get()->FindVisibleNotificationById(
          notification_id()));

  if (inline_reply()) {
    SkColor text_color = ash::AshColorProvider::Get()->GetContentLayerColor(
        ash::AshColorProvider::ContentLayerType::kTextColorSecondary);
    inline_reply()->textfield()->SetTextColor(text_color);
    inline_reply()->textfield()->set_placeholder_text_color(text_color);
  }

  if (icon_view() &&
      (right_content()->width() - icon_view()->GetImageDrawingSize().width() >
           kSmallImageBackgroundThreshold ||
       right_content()->height() - icon_view()->GetImageDrawingSize().height() >
           kSmallImageBackgroundThreshold)) {
    icon_view()->set_apply_rounded_corners(false);
    right_content()->SetBackground(views::CreateRoundedRectBackground(
        ash::AshColorProvider::Get()->GetControlsLayerColor(
            ash::AshColorProvider::ControlsLayerType::
                kControlBackgroundColorInactive),
        message_center::kImageCornerRadius));
  }
}

std::unique_ptr<message_center::NotificationInputContainer>
AshNotificationView::GenerateNotificationInputContainer() {
  return std::make_unique<AshNotificationInputContainer>(this);
}

std::unique_ptr<views::LabelButton>
AshNotificationView::GenerateNotificationLabelButton(
    views::Button::PressedCallback callback,
    const std::u16string& label) {
  std::unique_ptr<views::LabelButton> actions_button =
      std::make_unique<PillButton>(std::move(callback), label,
                                   PillButton::Type::kAccentFloatingWithoutIcon,
                                   /*icon=*/nullptr,
                                   kNotificationPillButtonHorizontalSpacing);
  return actions_button;
}

gfx::Size AshNotificationView::GetIconViewSize() const {
  return gfx::Size(kIconViewSize, kIconViewSize);
}

int AshNotificationView::GetLargeImageViewMaxWidth() const {
  return message_center::kNotificationWidth - kNotificationViewPadding.width() -
         kAppIconViewSize - kMainRightViewChildPadding.width();
}

void AshNotificationView::ToggleInlineSettings(const ui::Event& event) {
  if (!inline_settings_enabled())
    return;

  bool should_show_inline_settings = !inline_settings_row()->GetVisible();
  PerformToggleInlineSettingsAnimation(should_show_inline_settings);

  NotificationViewBase::ToggleInlineSettings(event);

  if (is_grouped_parent_view_) {
    if (shown_in_popup_) {
      grouped_notifications_scroll_view_->SetVisible(
          !should_show_inline_settings);
    } else {
      grouped_notifications_container_->SetVisible(
          !should_show_inline_settings);
    }
  } else {
    // In settings UI, we only show the app icon and header row along with the
    // inline settings UI.
    header_row()->SetVisible(true);
    left_content()->SetVisible(!should_show_inline_settings);
    right_content()->SetVisible(!should_show_inline_settings);
  }
  expand_button_->SetVisible(!should_show_inline_settings);

  PreferredSizeChanged();
}

void AshNotificationView::OnInlineReplyUpdated() {
  DCHECK(inline_reply() && inline_reply()->GetVisible());
  // Fade out actions button and then fade in inline reply.
  message_center_utils::InitLayerForAnimations(action_buttons_row());
  message_center_utils::FadeOutView(
      action_buttons_row(),
      base::BindOnce(
          [](base::WeakPtr<ash::AshNotificationView> parent,
             views::View* action_buttons_row) {
            if (parent) {
              action_buttons_row->layer()->SetOpacity(1.0f);
              action_buttons_row->SetVisible(false);
            }
          },
          weak_factory_.GetWeakPtr(), action_buttons_row()),
      /*delay_in_ms=*/0, kActionButtonsFadeOutAnimationDurationMs,
      gfx::Tween::LINEAR,
      "Ash.NotificationView.ActionButtonsRow.FadeOut.AnimationSmoothness");

  // Delay for the action buttons to fade out, then fade in inline reply.
  message_center_utils::InitLayerForAnimations(inline_reply());
  message_center_utils::FadeInView(
      inline_reply(), kActionButtonsFadeOutAnimationDurationMs,
      kInlineReplyFadeInAnimationDurationMs, gfx::Tween::LINEAR,
      "Ash.NotificationView.InlineReply.FadeIn.AnimationSmoothness");
}

views::View* AshNotificationView::FindGroupNotificationView(
    const std::string& notification_id) {
  auto notification = base::ranges::find(
      grouped_notifications_container_->children(), notification_id,
      [](views::View* notification_view) {
        return static_cast<AshNotificationView*>(notification_view)
            ->notification_id();
      });
  return notification == grouped_notifications_container_->children().end()
             ? nullptr
             : *notification;
}

void AshNotificationView::OnNotificationRemoved(
    const std::string& notification_id,
    bool by_user) {
  if (!is_grouped_parent_view_)
    return;

  RemoveGroupNotification(notification_id);
}

void AshNotificationView::OnWidgetClosing(views::Widget* widget) {
  widget_observation_.Reset();
  AbortAllAnimations();
}

void AshNotificationView::OnWidgetDestroying(views::Widget* widget) {
  OnWidgetClosing(widget);
}

void AshNotificationView::AbortAllAnimations() {
  std::vector<scoped_refptr<ui::LayerAnimator>> animators;
  animators.push_back(layer()->GetAnimator());
  for (auto* child_notification :
       grouped_notifications_container_->children()) {
    animators.push_back(child_notification->layer()->GetAnimator());
  }

  for (auto animator : animators) {
    animator->AbortAllAnimations();
  }
}

void AshNotificationView::CreateOrUpdateSnoozeButton(
    const message_center::Notification& notification) {
  if (!notification.should_show_snooze_button()) {
    if (action_buttons_row()->Contains(snooze_button_)) {
      action_buttons_row()->RemoveChildViewT(snooze_button_);
      snooze_button_ = nullptr;
      DCHECK(action_buttons_row()->Contains(snooze_button_spacer_));
      action_buttons_row()->RemoveChildViewT(snooze_button_spacer_);
      snooze_button_spacer_ = nullptr;
    }
    return;
  }

  if (snooze_button_) {
    DCHECK(snooze_button_spacer_);
    // Spacer and snooze button should be at the end of action buttons row.
    action_buttons_row()->ReorderChildView(
        snooze_button_spacer_, action_buttons_row()->children().size());
    action_buttons_row()->ReorderChildView(
        snooze_button_, action_buttons_row()->children().size());
    return;
  }

  action_buttons_row()->AddChildView(
      views::Builder<views::BoxLayoutView>()
          .CopyAddressTo(&snooze_button_spacer_)
          .SetMainAxisAlignment(MainAxisAlignment::kEnd)
          .SetProperty(
              views::kFlexBehaviorKey,
              views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
                                       views::MaximumFlexSizeRule::kUnbounded))
          .Build());

  auto snooze_button = std::make_unique<IconButton>(
      base::BindRepeating(&AshNotificationView::OnSnoozeButtonPressed,
                          base::Unretained(this)),
      IconButton::Type::kSmallFloating, &kNotificationSnoozeButtonIcon,
      IDS_MESSAGE_CENTER_NOTIFICATION_SNOOZE_BUTTON_TOOLTIP);
  snooze_button_ = action_buttons_row()->AddChildView(std::move(snooze_button));
}

void AshNotificationView::UpdateGroupedNotificationsVisibility() {
  for (size_t i = 0; i < grouped_notifications_container_->children().size();
       i++) {
    auto* view = grouped_notifications_container_->children()[i];
    bool show_notification_view =
        IsExpanded() ||
        i < message_center_style::kMaxGroupedNotificationsInCollapsedState;

    if (view->GetVisible() == show_notification_view)
      continue;

    view->SetVisible(show_notification_view);
  }
}

void AshNotificationView::UpdateMessageLabelInExpandedState(
    const message_center::Notification& notification) {
  if (notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS ||
      notification.message().empty()) {
    message_label_in_expanded_state_->SetVisible(false);
    return;
  }
  message_label_in_expanded_state_->SetText(gfx::TruncateString(
      notification.message(), message_center::kMessageCharacterLimit,
      gfx::WORD_BREAK));

  message_label_in_expanded_state_->SetVisible(true);
}

void AshNotificationView::UpdateBackground(int top_radius, int bottom_radius) {
  SkColor background_color;
  if (shown_in_popup_) {
    background_color = AshColorProvider::Get()->GetBaseLayerColor(
        AshColorProvider::BaseLayerType::kTransparent80);
  } else {
    background_color = AshColorProvider::Get()->GetControlsLayerColor(
        AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive);
  }

  if (background_color == background_color_ && top_radius_ == top_radius &&
      bottom_radius_ == bottom_radius) {
    return;
  }

  if (!is_grouped_child_view_)
    background_color_ = background_color;
  top_radius_ = top_radius;
  bottom_radius_ = bottom_radius;

  SetBackground(views::CreateBackgroundFromPainter(
      std::make_unique<message_center::NotificationBackgroundPainter>(
          top_radius_, bottom_radius_, background_color_)));
}

int AshNotificationView::GetExpandedMessageLabelWidth() {
  int notification_width = shown_in_popup_ ? message_center::kNotificationWidth
                                           : kNotificationInMessageCenterWidth;

  return notification_width - kNotificationViewPadding.width() -
         kAppIconViewSize - kMainRightViewChildPadding.width() -
         kMessageLabelInExpandedStatePadding.width();
}

void AshNotificationView::DisableNotification() {
  message_center::MessageCenter::Get()->DisableNotification(notification_id());
}

void AshNotificationView::UpdateAppIconView(
    const message_center::Notification* notification) {
  // Grouped child notification use notification's icon for the app icon view,
  // so we don't need further update here.
  if (!notification ||
      (is_grouped_child_view_ && !notification->icon().IsEmpty()))
    return;

  SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
      AshColorProvider::ContentLayerType::kInvertedButtonLabelColor);
  SkColor icon_background_color = CalculateIconAndButtonsColor(notification);

  // TODO(crbug.com/768748): figure out if this has a performance impact and
  // cache images if so.
  gfx::Image masked_small_icon = notification->GenerateMaskedSmallIcon(
      kAppIconImageSize, icon_color, icon_background_color, icon_color);

  gfx::ImageSkia app_icon =
      masked_small_icon.IsEmpty()
          ? gfx::CreateVectorIcon(message_center::kProductIcon,
                                  kAppIconImageSize, icon_color)
          : masked_small_icon.AsImageSkia();

  app_icon_view_->SetImage(
      gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
          kAppIconViewSize / 2, icon_background_color, app_icon));
}

SkColor AshNotificationView::CalculateIconAndButtonsColor(
    const message_center::Notification* notification) {
  SkColor default_color = AshColorProvider::Get()->GetControlsLayerColor(
      AshColorProvider::ControlsLayerType::kControlBackgroundColorActive);

  if (!notification)
    return default_color;

  auto color_id = notification->accent_color_id();
  absl::optional<SkColor> accent_color = notification->accent_color();

  if ((!color_id || !GetWidget()) && !accent_color.has_value())
    return default_color;

  SkColor fg_color;
  // ColorProvider needs widget to be created.
  if (color_id && GetWidget()) {
    fg_color = GetColorProvider()->GetColor(color_id.value());
  } else {
    fg_color = accent_color.value();
  }

  // TODO(crbug/1351205): move color calculation logic to color mixer.
  // TODO(crbug/1294459): re-evaluate contrast, maybe increase or use fixed HSL
  float minContrastRatio =
      DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
          ? minContrastRatio = kDarkModeMinContrastRatio
          : color_utils::kMinimumReadableContrastRatio;

  // Actual color is kTransparent80, but BlendForMinContrast requires opaque.
  SkColor bg_color = AshColorProvider::Get()->GetBaseLayerColor(
      AshColorProvider::BaseLayerType::kOpaque);
  return color_utils::BlendForMinContrast(
             fg_color, bg_color,
             /*high_contrast_foreground=*/absl::nullopt, minContrastRatio)
      .color;
}

void AshNotificationView::UpdateIconAndButtonsColor(
    const message_center::Notification* notification) {
  UpdateAppIconView(notification);

  SkColor icon_color = CalculateIconAndButtonsColor(notification);
  SkColor button_color = icon_color;
  bool use_default_button_color =
      !notification ||
      notification->rich_notification_data().ignore_accent_color_for_text;
  if (use_default_button_color) {
    button_color = AshColorProvider::Get()->GetControlsLayerColor(
        AshColorProvider::ControlsLayerType::kControlBackgroundColorActive);
  }

  for (auto* action_button : action_buttons()) {
    static_cast<PillButton*>(action_button)->SetButtonTextColor(button_color);
  }

  if (snooze_button_)
    snooze_button_->SetIconColor(button_color);
}

void AshNotificationView::AnimateResizeAfterRemoval(
    views::View* to_be_removed) {
  auto on_resize_complete = base::BindRepeating(
      [](base::WeakPtr<AshNotificationView> self) {
        if (!self)
          return;

        self->set_is_animating(false);

        if (self->shown_in_popup_) {
          self->grouped_notifications_scroll_view_->Layout();
        }
      },
      weak_factory_.GetWeakPtr());

  int group_container_previous_height =
      grouped_notifications_container_->height();
  size_t removed_index =
      grouped_notifications_container_->GetIndexOf(to_be_removed).value();

  grouped_notifications_container_->RemoveChildViewT(to_be_removed).reset();

  auto* notification_view_controller = message_center_utils::
      GetActiveNotificationViewControllerForNotificationView(this);
  if (notification_view_controller)
    notification_view_controller->AnimateResize();

  if (shown_in_popup_) {
    grouped_notifications_scroll_view_->Layout();
  } else {
    Layout();
    PreferredSizeChanged();
  }

  int grouped_container_height_reduction =
      group_container_previous_height -
      grouped_notifications_container_->height();

  views::AnimationBuilder animation_builder;

  if (grouped_notifications_container_->children().begin() + removed_index >=
      grouped_notifications_container_->children().end()) {
    return;
  }
  set_is_animating(true);
  animation_builder.OnEnded(on_resize_complete);
  for (auto it =
           grouped_notifications_container_->children().begin() + removed_index;
       it != grouped_notifications_container_->children().end(); it++) {
    gfx::Rect child_bounds = (*it)->layer()->GetTargetBounds();
    (*it)->layer()->SetBounds(gfx::Rect(
        child_bounds.x(), child_bounds.y() + grouped_container_height_reduction,
        child_bounds.width(), child_bounds.height()));

    animation_builder.Once()
        .SetDuration(base::Milliseconds(
            message_center::kNotificationResizeAnimationDurationMs))
        .SetBounds((*it), child_bounds, gfx::Tween::EASE_OUT);
  }
}

void AshNotificationView::PerformExpandCollapseAnimation() {
  if (title_row_)
    title_row_->PerformExpandCollapseAnimation();

  // Fade in `header row()` if this is not a grouped parent view.
  if (header_row() && header_row()->GetVisible() && !is_grouped_parent_view_) {
    message_center_utils::FadeInView(
        header_row(), kHeaderRowFadeInAnimationDelayMs,
        kHeaderRowFadeInAnimationDurationMs, gfx::Tween::LINEAR,
        "Ash.NotificationView.HeaderRow.FadeIn.AnimationSmoothness");
  }

  // Fade in `message_label()`. We only do fade in for both message view in
  // expanded and collapsed mode if there's a difference between them (a.k.a
  // when `message_label()` is truncated).
  if (message_label() && message_label()->GetVisible() &&
      IsMessageLabelTruncated()) {
    message_center_utils::InitLayerForAnimations(message_label());
    message_center_utils::FadeInView(
        message_label(), kMessageLabelFadeInAnimationDelayMs,
        kMessageLabelFadeInAnimationDurationMs, gfx::Tween::LINEAR,
        "Ash.NotificationView.MessageLabel.FadeIn.AnimationSmoothness");
  }

  // Fade in `message_label_in_expanded_state_`.
  if (message_label_in_expanded_state_ &&
      message_label_in_expanded_state_->GetVisible() && message_label() &&
      IsMessageLabelTruncated()) {
    message_center_utils::FadeInView(
        message_label_in_expanded_state_,
        kMessageLabelInExpandedStateFadeInAnimationDelayMs,
        kMessageLabelInExpandedStateFadeInAnimationDurationMs,
        gfx::Tween::LINEAR,
        "Ash.NotificationView.ExpandedMessageLabel.FadeIn.AnimationSmoothness");
  }

  if (!image_container_view()->children().empty()) {
    PerformLargeImageAnimation();
  }

  if (actions_row() && actions_row()->GetVisible()) {
    message_center_utils::FadeInView(
        actions_row(), kActionsRowFadeInAnimationDelayMs,
        kActionsRowFadeInAnimationDurationMs, gfx::Tween::LINEAR,
        "Ash.NotificationView.ActionsRow.FadeIn.AnimationSmoothness");
  }

  if (total_grouped_notifications_) {
    // Ensure layout is up-to-date before animating expand button. This is used
    // for its bounds animation.
    if (needs_layout())
      Layout();
    DCHECK(!needs_layout());

    expand_button_->AnimateExpandCollapse();
  }
}

void AshNotificationView::PerformLargeImageAnimation() {
  message_center_utils::InitLayerForAnimations(image_container_view());
  message_center_utils::InitLayerForAnimations(icon_view());
  auto icon_view_bounds = icon_view()->GetBoundsInScreen();
  auto large_image_bounds = image_container_view()->GetBoundsInScreen();

  if (IsExpanded()) {
    // In expanded state, do a scale and translate from `icon_view()` to
    // `image_container_view()`.
    message_center_utils::InitLayerForAnimations(image_container_view());
    ScaleAndTranslateView(image_container_view(),
                          static_cast<double>(icon_view()->width()) /
                              image_container_view()->width(),
                          static_cast<double>(icon_view()->height()) /
                              image_container_view()->height(),
                          icon_view_bounds.x() - large_image_bounds.x(),
                          icon_view_bounds.y() - large_image_bounds.y(),
                          "Ash.NotificationView.ImageContainerView."
                          "ScaleAndTranslate.AnimationSmoothness");

    // If we use different images for `icon_view()` and `image_container_view()`
    // (a.k.a hide_icon_on_expanded() is false), fade in
    // `image_container_view()`.
    if (!hide_icon_on_expanded()) {
      message_center_utils::FadeInView(
          image_container_view(), kLargeImageFadeInAnimationDelayMs,
          kLargeImageFadeInAnimationDurationMs, gfx::Tween::LINEAR,
          "Ash.NotificationView.ImageContainerView.FadeIn.AnimationSmoothness");
    }
    return;
  }

  if (hide_icon_on_expanded()) {
    // In collapsed state, if we use a same image for `icon_view()` and
    // `image_container_view()`, perform a scale and translate from
    // `image_container_view()` to `icon_view()`.
    ScaleAndTranslateView(
        icon_view(),
        static_cast<double>(image_container_view()->width()) /
            icon_view()->width(),
        static_cast<double>(image_container_view()->height()) /
            icon_view()->height(),
        large_image_bounds.x() - icon_view_bounds.x(),
        large_image_bounds.y() - icon_view_bounds.y(),
        "Ash.NotificationView.IconView.ScaleAndTranslate.AnimationSmoothness");
    return;
  }

  // In collapsed state, if we use a different image for `icon_view()` and
  // `image_container_view()`, fade out and scale down `image_container_view()`.
  message_center_utils::FadeOutView(
      image_container_view(),
      base::BindRepeating(
          [](base::WeakPtr<ash::AshNotificationView> parent,
             views::View* image_container_view) {
            if (parent) {
              image_container_view->layer()->SetOpacity(1.0f);
              //
              image_container_view->layer()->SetTransform(gfx::Transform());
              image_container_view->SetVisible(false);
            }
          },
          weak_factory_.GetWeakPtr(), image_container_view()),
      kLargeImageFadeOutAnimationDelayMs, kLargeImageFadeOutAnimationDurationMs,
      gfx::Tween::ACCEL_20_DECEL_100,
      "Ash.NotificationView.ImageContainerView.FadeOut.AnimationSmoothness");

  gfx::Transform transform;
  // Translate y further down so that it would not interfere with the currently
  // shown `icon_view()`.
  transform.Translate((icon_view_bounds.x() - large_image_bounds.x()),
                      (icon_view_bounds.y() - large_image_bounds.y() +
                       large_image_bounds.height()));
  transform.Scale(static_cast<double>(icon_view()->width()) /
                      image_container_view()->width(),
                  static_cast<double>(icon_view()->height()) /
                      image_container_view()->height());

  ui::AnimationThroughputReporter reporter(
      image_container_view()->layer()->GetAnimator(),
      ash::metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) {
        base::UmaHistogramPercentage(
            "Ash.NotificationView.ImageContainerView.ScaleDown."
            "AnimationSmoothness",
            smoothness);
      })));

  views::AnimationBuilder()
      .SetPreemptionStrategy(
          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
      .Once()
      .At(base::TimeDelta())
      .SetTransform(image_container_view(), gfx::Transform())
      .Then()
      .SetDuration(base::Milliseconds(kLargeImageScaleDownDurationMs))
      .SetTransform(image_container_view(), transform,
                    gfx::Tween::ACCEL_20_DECEL_100);
}

void AshNotificationView::PerformToggleInlineSettingsAnimation(
    bool should_show_inline_settings) {
  if (ui::ScopedAnimationDurationScaleMode::duration_multiplier() ==
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)
    return;

  message_center_utils::InitLayerForAnimations(main_right_view_);
  message_center_utils::InitLayerForAnimations(inline_settings_row());

  // Fade out views.
  if (should_show_inline_settings) {
    // Fade out left_content if it's visible.
    if (left_content_->GetVisible()) {
      message_center_utils::InitLayerForAnimations(left_content());
      message_center_utils::FadeOutView(
          left_content(),
          base::BindRepeating(
              [](base::WeakPtr<ash::AshNotificationView> parent,
                 views::View* left_content) {
                if (parent) {
                  left_content->layer()->SetOpacity(1.0f);
                  left_content->SetVisible(false);
                }
              },
              weak_factory_.GetWeakPtr(), left_content()),
          /*delay_in_ms=*/0, kToggleInlineSettingsFadeOutDurationMs,
          gfx::Tween::LINEAR,
          "Ash.NotificationView.LeftContent.FadeOut.AnimationSmoothness");
    }
    message_center_utils::FadeOutView(
        expand_button_,
        base::BindRepeating(
            [](base::WeakPtr<ash::AshNotificationView> parent,
               views::View* expand_button) {
              if (parent) {
                expand_button->layer()->SetOpacity(1.0f);
                expand_button->SetVisible(false);
              }
            },
            weak_factory_.GetWeakPtr(), expand_button_),
        /*delay_in_ms=*/0, kToggleInlineSettingsFadeOutDurationMs,
        gfx::Tween::LINEAR,
        "Ash.NotificationView.ExpandButton.FadeOut.AnimationSmoothness");

    // Fade out icon_view() if it exists.
    if (icon_view()) {
      message_center_utils::InitLayerForAnimations(icon_view());
      message_center_utils::FadeOutView(
          icon_view(),
          base::BindRepeating(
              [](base::WeakPtr<ash::AshNotificationView> parent,
                 views::View* icon_view) {
                if (parent) {
                  icon_view->layer()->SetOpacity(1.0f);
                  icon_view->SetVisible(false);
                }
              },
              weak_factory_.GetWeakPtr(), icon_view()),
          /*delay_in_ms=*/0, kToggleInlineSettingsFadeOutDurationMs,
          gfx::Tween::LINEAR,
          "Ash.NotificationView.IconView.FadeOut.AnimationSmoothness");
    }
  } else {
    message_center_utils::FadeOutView(
        inline_settings_row(),
        base::BindRepeating(
            [](base::WeakPtr<ash::AshNotificationView> parent,
               views::View* inline_settings_row) {
              if (parent) {
                inline_settings_row->layer()->SetOpacity(1.0f);
                inline_settings_row->SetVisible(false);
              }
            },
            weak_factory_.GetWeakPtr(), inline_settings_row()),
        /*delay_in_ms=*/0, kToggleInlineSettingsFadeOutDurationMs,
        gfx::Tween::LINEAR,
        "Ash.NotificationView.InlineSettingsRow.FadeOut.AnimationSmoothness");
  }

  // Fade in views.
  message_center_utils::FadeInView(
      main_right_view_, kToggleInlineSettingsFadeInDelayMs,
      kToggleInlineSettingsFadeInDurationMs, gfx::Tween::LINEAR,
      "Ash.NotificationView.MainRightView.FadeIn.AnimationSmoothness");
}

void AshNotificationView::AnimateSingleToGroupFadeIn() {
  auto* fade_in_view = shown_in_popup_ ? grouped_notifications_scroll_view_
                                       : grouped_notifications_container_;
  message_center_utils::InitLayerForAnimations(fade_in_view);
  message_center_utils::FadeInView(
      fade_in_view, /*delay_in_ms=*/0,
      kConvertFromSingleToGroupFadeInDurationMs, gfx::Tween::LINEAR,
      "Ash.NotificationView.ConvertSingleToGroup.FadeIn.AnimationSmoothness");
}

int AshNotificationView::CalculateMaxHeightForGroupedNotifications() {
  auto* shelf = Shell::GetPrimaryRootWindowController()->shelf();
  const WorkAreaInsets* work_area =
      WorkAreaInsets::ForWindow(shelf->GetWindow()->GetRootWindow());

  const int bottom = shelf->IsHorizontalAlignment()
                         ? shelf->GetShelfBoundsInScreen().y()
                         : work_area->user_work_area_bounds().bottom();

  const int free_space_height_above_anchor =
      bottom - work_area->user_work_area_bounds().y();

  const int vertical_margin = 2 * message_center::kMarginBetweenPopups +
                              kNotificationViewPadding.height();

  return free_space_height_above_anchor - main_view_->bounds().height() -
         vertical_margin;
}

bool AshNotificationView::IsMessageLabelTruncated() {
  // True if the expanded label has more than one line.
  if (message_label_in_expanded_state_->GetRequiredLines() > 1)
    return true;

  // Get the first row's width of `message_label_in_expanded_state_`'s text,
  // which is also the text width of this label since it has one line. If text
  // width is larger than `left_content()`'s width, which is the space dedicated
  // to `message_label()`, the text is truncated.
  int text_width =
      message_label_in_expanded_state_
          ->GetSubstringBounds(gfx::Range(
              0, message_label_in_expanded_state_->GetText().length()))
          .front()
          .width();
  return text_width > left_content()->width();
}

}  // namespace ash

redpajama_set_name: RedPajamaGithub


A few years ago, I traveled extensively through central Turkey photographing the ancient archeologic sites of the Hittites and of the origins of human society and agriculture. These locations included Hattusha, Shapinuva and Chatal Hoyuk. I used these photographs as the background for two large-scale art works in the Cosmic Consciousness series. These gigantic, 10 x 20 foot pieces were commissioned by the University of Chicago and were installed at the University of Chicago's Rockefeller Memorial Chapel, and later at the Oriental Institute.
Veil includes hundreds of photographs that I took from the excavations in Chatal Hoyuk, the oldest known city, over 10,000 years old, located near Konya. This location may be the actual origin of cities and of agriculture.
The Hittite language is the oldest known Indo-European language. Cassiopeia has hundreds of photographs from the Hittite world, a mixture from the administrative capital of Hattusha, and the current archeological diggings at Shapinuva, the religious center. The Shapinuva photographs include images of Aygul Suel and her archeological team.
These pieces deal with the source of our cosmic existence, and of our cultural origins. Galactic nebula structures, with thousands of neuronal profiles, intersect with the photographs. Cosmic origins, stellar origins and cultural origins overlap and intertwine.
At the Consulate, much smaller versions of these two pieces will be displayed, along with reproductions of the underlying photographs and neuronal patterns.
At the opening reception I will give a talk explaining these two pieces, and the possible link between ancient Turkey and Lithuania. This particular historical link involves Dr. Jonas Basanavicius, the father of Lithuania's independence.
At the opening reception space is limited, so advanced registration is necessary.


Do You Want To Be A Fatherless Daughter Movement Mentor?
Are you a fatherless daughter, or do you love someone who is fatherless? If so, you may be the perfect mentor. Picture your favorite small group/book club with the topic being all things fatherless. You don't need to be a therapist..you just need to be someone who has been greatly affected by fatherlessness and feel lead to help.
Our mission is to have multiple mentors in every single city, state and country around the world. We are going BIG. Do you want to join our movement?
1) You can attend a LIVE quarterly event in person in Atlanta, Georgia. This event is dedicated to ALL Fatherless Daughters. We will be offering a day of healing, education and sisterhood. At the end of the of the program, you can decide if you would like to become a Fatherless Daughter Movement Mentor.
2) You can attend our mentorship class virtually. The program will be sent directly to your email for you to learn in the comfort of your own home.
Because this mentorship tribe is so special, we are asking that you scroll down to fill out the short form to be considered for the mentorship program. There is no need to fill out the form if you will be attending as an observer without interest in the mentorship program. You can still take part in one of our monumental programs.
https://dennababul.com/wp-content/uploads/FullSizeRender-9.jpg 736 738 Denna Babul https://dennababul.com/wp-content/uploads/logo-2.png Denna Babul2017-09-07 12:31:272017-09-07 12:31:27Do You Want To Be A Fatherless Daughter Movement Mentor?
Heal Yourself and Heal Others: Become a Fatherless Daughter Mentor!
Celebrity Dance Challenge on Behalf of EDIN!
Parenting: Is it harder after the first one?
Denna's Book Club Pick of the month!
Should You Keep Him or Get Rid of Him? Could Your Broken Heart Be A Self-Fulfilled Prophecy?


text:
class AddCommentCounterToPosts < ActiveRecord::Migration[5.0]
  def up
    add_column :posts, :comments_count, :integer, default: 0

    Post.reset_column_information
    Post.find_each.each do |p|
      p.update_attribute :comments_count, p.comments.length
    end
  end

  def down
    remove_column :posts, :comments_count
  end
end


text:
FashionShopping
Shop Talk: Pilgrim, Moo Shoes, Mandate of Heaven and More
By Jessica Delfino
Jessica Delfino
The Pilgrim store front on Orchard Street.
Here's Lo-Down contributor Jessica Delfino's weekly roundup of Lower East Side shops, sales and new collections to keep your eye on.
Pilgrim (75 Orchard Street) is the classiest vintage store around, carrying over thirty brands of gently used handbags by Chanel, Dior and Valentino, to name a few. There's plenty of fancy jewelry and elegant numbers you swear you've seen in your cool great aunt's closet.
Sometimes I like to strut around and imagine I'm the type of woman who wears these fine things on my day to day — en route to Bloomingdale's for a lady's shopping trip, or on my way to a fundraiser for the non-profit that my friends and I started for kicks. They don't often have sales, but stay tuned for a jewelry sale in the coming weeks.
Pilgrim is open Sunday, Tuesday and Wednesday, 12 p.m. to 6 p.m., Monday 12 to 5 p.m. and Thursday through Saturday 12 p.m. to 7 p.m.
Mandate of Heaven (17 Essex Street) is located just a few steps above the Essex Street sidewalk in an unexpected storefront. The store mainly consists of reconstructed vintage—new fashions lovingly reimagined using the guts of older pieces–as well as some gently used items, and is awash owith pastels, short suits, lace tops, plaids and mint denim made for tiny little ladies who I imagine have tea parties and carry parasols. The unique designs hover in the $200-$300 range. If human beings were dolls, we'd all shop at Mandate of Heaven.
Mandate of Heaven is open Wednesday, Thursday and Sunday 12 p.m. to 8:30 p.m., Friday and Saturday, 12 p.m. to 9 p.m.
Moo Shoes Vegan Shop Up Event will rock your fair trade socks off this Sunday with a slew of vendors hocking vegan wares such as Yeah Dawg, Cake Thieves, Tamerlaine Farm, Meow Meow Tweet, Sweet Maresa and Mountain Morsels. You can conscientiously shop and they'll do you one better — your purchases will help raise funds for Animal Sanctuary at Tamerlaine Farm's barn renovations, so they can house the chicks and turkeys they plan to rescue in the Fall.
The sale takes place at Moo Shoes, 78 Orchard Street, from 12 noon to 7 p.m. on Sunday, August 10th and again on September 21st.
Soho Sample Sale at Open House Gallery (201 Mulberry, between Kenmare & Spring) will be wrapping up their week long run in Soho on Friday August 15th. If you're looking for deals on mixed contemporary men's & ladies' European and American brands at a substantial discount, here's an excellent spot to find them.
Open House Gallery is open 9 a.m. to 6 p.m., Monday through Friday, closed weekends.
Gone But Not Forgotten: I really miss Old Hollywood. I bought one of their cabinets and a picture frame when they cleared out. Their clothing, jewelry and strange bric-a-brac was a mixture of unexplainably cheap and weirdly expensive, which I appreciate in a store because I like options. Sure, they're still in Brooklyn (Greenpoint, to be exact) but I want them back within walking distance. Who do I have to talk to to make this happen…DeBlasio?
Alternately, though I didn't really shop at Earnest Sewn (formerly at 90 Orchard) as much, I liked their vibe. Now the entire corner where they once sat is shuttered, leaving the intersection of Orchard and Broome looking haunted. There are quite a few empty storefronts around the Lower East Side at the moment, but when new businesses come in, chances are, they'll be a bar or a clothing store, so at least we have new places to look forward to.
What clothing stores that are no longer with us do you miss most?
Send your tips about upcoming sales and new collections to: tips@TheLoDownNY.com
Jessica Delfino is a comedic musician who has lived and performed in the LES for like, forever. She first fell in love with beautiful clothing as a kid when her mom let her dress up in her vintage stash. Jessica was called "One of NY's 50 Most Fashionable People" by Time Out NY. Check her out at here or email her at: jessica@thelodownny.com. She's also @JessicaDelfino on Twitter.
lower east side shopping
"Live Healthy" Classes Bring Outdoor Fitness and Diet Tips to the L.E.S.
Sponsored Post: Halstead Apartment of the Week
Fashion Week on the Lower East Side
Arts The Lo-Down - October 3, 2022
Hester Street Fair Kicks off New Season at The Seaport
Arts Traven Rice - April 22, 2022
Ludlow Flea Continues Through October
Events The Lo-Down - October 3, 2021
Old Jewish Men Pop-Up Shop Comes to Orchard Street
Arts Traven Rice - July 24, 2021

redpajama_set_name: RedPajamaCommonCrawl