12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/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/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);
- if (!parent_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);
- // Here we need to check if `inline_reply()` is still valid since user
- // can click the expand button when the view is being destructed, which
- // invalidate `inline_reply()`.
- if (inline_reply() && inline_reply()->GetVisible()) {
- // If inline reply is visible, fade it out then set expanded.
- 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(¬ification);
- 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(¬ification);
- }
- 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::kIconlessAccentFloating,
- /*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::ActionButtonPressed(size_t index,
- const ui::Event& event) {
- NotificationViewBase::ActionButtonPressed(index, event);
- // If inline reply is visible, fade out actions button and then fade in inline
- // reply. Here we need to check if `inline_reply()` is still valid since user
- // can click an action button when the view is being destructed, which
- // invalidate `inline_reply()`.
- if (inline_reply() && inline_reply()->GetVisible()) {
- 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 id_match = [¬ification_id](views::View* notification_view) {
- return notification_id ==
- static_cast<AshNotificationView*>(notification_view)
- ->notification_id();
- };
- auto notification = std::find_if(
- grouped_notifications_container_->children().begin(),
- grouped_notifications_container_->children().end(), id_match);
- 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
|