| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |  | 
| 2 // Use of this source code is governed by a BSD-style license that can be |  | 
| 3 // found in the LICENSE file. |  | 
| 4 |  | 
| 5 #include "ui/arc/notification/arc_custom_notification_view.h" |  | 
| 6 |  | 
| 7 #include "ash/wm/window_util.h" |  | 
| 8 #include "base/auto_reset.h" |  | 
| 9 #include "base/memory/ptr_util.h" |  | 
| 10 #include "components/exo/notification_surface.h" |  | 
| 11 #include "components/exo/surface.h" |  | 
| 12 #include "ui/accessibility/ax_action_data.h" |  | 
| 13 #include "ui/accessibility/ax_node_data.h" |  | 
| 14 #include "ui/arc/notification/arc_notification_view.h" |  | 
| 15 #include "ui/base/l10n/l10n_util.h" |  | 
| 16 #include "ui/base/resource/resource_bundle.h" |  | 
| 17 #include "ui/compositor/layer_animation_observer.h" |  | 
| 18 #include "ui/events/event_handler.h" |  | 
| 19 #include "ui/gfx/animation/linear_animation.h" |  | 
| 20 #include "ui/gfx/animation/tween.h" |  | 
| 21 #include "ui/gfx/canvas.h" |  | 
| 22 #include "ui/gfx/transform.h" |  | 
| 23 #include "ui/message_center/message_center_style.h" |  | 
| 24 #include "ui/message_center/views/toast_contents_view.h" |  | 
| 25 #include "ui/strings/grit/ui_strings.h" |  | 
| 26 #include "ui/views/background.h" |  | 
| 27 #include "ui/views/focus/focus_manager.h" |  | 
| 28 #include "ui/views/layout/box_layout.h" |  | 
| 29 #include "ui/views/painter.h" |  | 
| 30 #include "ui/views/widget/root_view.h" |  | 
| 31 #include "ui/views/widget/widget.h" |  | 
| 32 #include "ui/wm/core/window_util.h" |  | 
| 33 |  | 
| 34 namespace arc { |  | 
| 35 |  | 
| 36 namespace { |  | 
| 37 |  | 
| 38 // This value should be the same as the duration of reveal animation of |  | 
| 39 // the settings view of an Android notification. |  | 
| 40 constexpr int kBackgroundColorChangeDuration = 360; |  | 
| 41 |  | 
| 42 SkColor GetControlButtonBackgroundColor( |  | 
| 43     const mojom::ArcNotificationShownContents& shown_contents) { |  | 
| 44   if (shown_contents == mojom::ArcNotificationShownContents::CONTENTS_SHOWN) |  | 
| 45     return message_center::kControlButtonBackgroundColor; |  | 
| 46   else |  | 
| 47     return SK_ColorTRANSPARENT; |  | 
| 48 } |  | 
| 49 |  | 
| 50 }  // namespace |  | 
| 51 |  | 
| 52 class ArcCustomNotificationView::EventForwarder : public ui::EventHandler { |  | 
| 53  public: |  | 
| 54   explicit EventForwarder(ArcCustomNotificationView* owner) : owner_(owner) {} |  | 
| 55   ~EventForwarder() override = default; |  | 
| 56 |  | 
| 57  private: |  | 
| 58   // ui::EventHandler |  | 
| 59   void OnEvent(ui::Event* event) override { |  | 
| 60     // Do not forward event targeted to the floating close button so that |  | 
| 61     // keyboard press and tap are handled properly. |  | 
| 62     if (owner_->floating_control_buttons_widget_ && event->target() && |  | 
| 63         owner_->floating_control_buttons_widget_->GetNativeWindow() == |  | 
| 64             event->target()) { |  | 
| 65       return; |  | 
| 66     } |  | 
| 67 |  | 
| 68     // TODO(yoshiki): Use a better tigger (eg. focusing EditText on |  | 
| 69     // notification) than clicking (crbug.com/697379). |  | 
| 70     if (event->type() == ui::ET_MOUSE_PRESSED) |  | 
| 71       owner_->ActivateToast(); |  | 
| 72 |  | 
| 73     views::Widget* widget = owner_->GetWidget(); |  | 
| 74     if (!widget) |  | 
| 75       return; |  | 
| 76 |  | 
| 77     // Forward the events to the containing widget, except for: |  | 
| 78     // 1. Touches, because View should no longer receive touch events. |  | 
| 79     //    See View::OnTouchEvent. |  | 
| 80     // 2. Tap gestures are handled on the Android side, so ignore them. |  | 
| 81     //    See crbug.com/709911. |  | 
| 82     // 3. Key events. These are already forwarded by NotificationSurface's |  | 
| 83     //    WindowDelegate. |  | 
| 84     if (event->IsLocatedEvent()) { |  | 
| 85       ui::LocatedEvent* located_event = event->AsLocatedEvent(); |  | 
| 86       located_event->target()->ConvertEventToTarget(widget->GetNativeWindow(), |  | 
| 87                                                     located_event); |  | 
| 88       if (located_event->type() == ui::ET_MOUSE_MOVED || |  | 
| 89           located_event->IsMouseWheelEvent()) { |  | 
| 90         widget->OnMouseEvent(located_event->AsMouseEvent()); |  | 
| 91       } else if (located_event->IsScrollEvent()) { |  | 
| 92         widget->OnScrollEvent(located_event->AsScrollEvent()); |  | 
| 93       } else if (located_event->IsGestureEvent() && |  | 
| 94                  event->type() != ui::ET_GESTURE_TAP) { |  | 
| 95         widget->OnGestureEvent(located_event->AsGestureEvent()); |  | 
| 96       } |  | 
| 97     } |  | 
| 98   } |  | 
| 99 |  | 
| 100   ArcCustomNotificationView* const owner_; |  | 
| 101 |  | 
| 102   DISALLOW_COPY_AND_ASSIGN(EventForwarder); |  | 
| 103 }; |  | 
| 104 |  | 
| 105 class ArcCustomNotificationView::SlideHelper |  | 
| 106     : public ui::LayerAnimationObserver { |  | 
| 107  public: |  | 
| 108   explicit SlideHelper(ArcCustomNotificationView* owner) : owner_(owner) { |  | 
| 109     GetSlideOutLayer()->GetAnimator()->AddObserver(this); |  | 
| 110 |  | 
| 111     // Reset opacity to 1 to handle to case when the surface is sliding before |  | 
| 112     // getting managed by this class, e.g. sliding in a popup before showing |  | 
| 113     // in a message center view. |  | 
| 114     if (owner_->surface_ && owner_->surface_->window()) |  | 
| 115       owner_->surface_->window()->layer()->SetOpacity(1.0f); |  | 
| 116   } |  | 
| 117   ~SlideHelper() override { |  | 
| 118     if (GetSlideOutLayer()) |  | 
| 119       GetSlideOutLayer()->GetAnimator()->RemoveObserver(this); |  | 
| 120   } |  | 
| 121 |  | 
| 122   void Update() { |  | 
| 123     const bool has_animation = |  | 
| 124         GetSlideOutLayer()->GetAnimator()->is_animating(); |  | 
| 125     const bool has_transform = !GetSlideOutLayer()->transform().IsIdentity(); |  | 
| 126     const bool sliding = has_transform || has_animation; |  | 
| 127     if (sliding_ == sliding) |  | 
| 128       return; |  | 
| 129 |  | 
| 130     sliding_ = sliding; |  | 
| 131 |  | 
| 132     if (sliding_) |  | 
| 133       OnSlideStart(); |  | 
| 134     else |  | 
| 135       OnSlideEnd(); |  | 
| 136   } |  | 
| 137 |  | 
| 138  private: |  | 
| 139   // This is a temporary hack to address crbug.com/718965 |  | 
| 140   ui::Layer* GetSlideOutLayer() { |  | 
| 141     ui::Layer* layer = owner_->parent()->layer(); |  | 
| 142     return layer ? layer : owner_->GetWidget()->GetLayer(); |  | 
| 143   } |  | 
| 144 |  | 
| 145   void OnSlideStart() { |  | 
| 146     if (!owner_->surface_ || !owner_->surface_->window()) |  | 
| 147       return; |  | 
| 148     surface_copy_ = ::wm::RecreateLayers(owner_->surface_->window()); |  | 
| 149     // |surface_copy_| is at (0, 0) in owner_->layer(). |  | 
| 150     surface_copy_->root()->SetBounds(gfx::Rect(surface_copy_->root()->size())); |  | 
| 151     owner_->layer()->Add(surface_copy_->root()); |  | 
| 152     owner_->surface_->window()->layer()->SetOpacity(0.0f); |  | 
| 153   } |  | 
| 154 |  | 
| 155   void OnSlideEnd() { |  | 
| 156     if (!owner_->surface_ || !owner_->surface_->window()) |  | 
| 157       return; |  | 
| 158     owner_->surface_->window()->layer()->SetOpacity(1.0f); |  | 
| 159     owner_->Layout(); |  | 
| 160     surface_copy_.reset(); |  | 
| 161   } |  | 
| 162 |  | 
| 163   // ui::LayerAnimationObserver |  | 
| 164   void OnLayerAnimationEnded(ui::LayerAnimationSequence* seq) override { |  | 
| 165     Update(); |  | 
| 166   } |  | 
| 167   void OnLayerAnimationAborted(ui::LayerAnimationSequence* seq) override { |  | 
| 168     Update(); |  | 
| 169   } |  | 
| 170   void OnLayerAnimationScheduled(ui::LayerAnimationSequence* seq) override {} |  | 
| 171 |  | 
| 172   ArcCustomNotificationView* const owner_; |  | 
| 173   bool sliding_ = false; |  | 
| 174   std::unique_ptr<ui::LayerTreeOwner> surface_copy_; |  | 
| 175 |  | 
| 176   DISALLOW_COPY_AND_ASSIGN(SlideHelper); |  | 
| 177 }; |  | 
| 178 |  | 
| 179 class ArcCustomNotificationView::ContentViewDelegate |  | 
| 180     : public ArcNotificationContentViewDelegate { |  | 
| 181  public: |  | 
| 182   explicit ContentViewDelegate(ArcCustomNotificationView* owner) |  | 
| 183       : owner_(owner) {} |  | 
| 184 |  | 
| 185   bool IsCloseButtonFocused() const override { |  | 
| 186     if (!owner_->close_button_) |  | 
| 187       return false; |  | 
| 188     return owner_->close_button_->HasFocus(); |  | 
| 189   } |  | 
| 190 |  | 
| 191   void RequestFocusOnCloseButton() override { |  | 
| 192     if (owner_->close_button_) |  | 
| 193       owner_->close_button_->RequestFocus(); |  | 
| 194     owner_->UpdateControlButtonsVisibility(); |  | 
| 195   } |  | 
| 196 |  | 
| 197   void UpdateControlButtonsVisibility() override { |  | 
| 198     owner_->UpdateControlButtonsVisibility(); |  | 
| 199   } |  | 
| 200 |  | 
| 201   void OnSlideChanged() override { |  | 
| 202     if (owner_->slide_helper_) |  | 
| 203       owner_->slide_helper_->Update(); |  | 
| 204   } |  | 
| 205 |  | 
| 206  private: |  | 
| 207   ArcCustomNotificationView* const owner_; |  | 
| 208 |  | 
| 209   DISALLOW_COPY_AND_ASSIGN(ContentViewDelegate); |  | 
| 210 }; |  | 
| 211 |  | 
| 212 ArcCustomNotificationView::ControlButton::ControlButton( |  | 
| 213     ArcCustomNotificationView* owner) |  | 
| 214     : message_center::PaddedButton(owner), owner_(owner) { |  | 
| 215   if (owner_->item_) { |  | 
| 216     set_background(views::Background::CreateSolidBackground( |  | 
| 217         GetControlButtonBackgroundColor(owner_->item_->GetShownContents()))); |  | 
| 218   } else { |  | 
| 219     set_background(views::Background::CreateSolidBackground( |  | 
| 220         message_center::kControlButtonBackgroundColor)); |  | 
| 221   } |  | 
| 222 } |  | 
| 223 |  | 
| 224 void ArcCustomNotificationView::ControlButton::OnFocus() { |  | 
| 225   message_center::PaddedButton::OnFocus(); |  | 
| 226   owner_->UpdateControlButtonsVisibility(); |  | 
| 227 } |  | 
| 228 |  | 
| 229 void ArcCustomNotificationView::ControlButton::OnBlur() { |  | 
| 230   message_center::PaddedButton::OnBlur(); |  | 
| 231   owner_->UpdateControlButtonsVisibility(); |  | 
| 232 } |  | 
| 233 |  | 
| 234 ArcCustomNotificationView::ArcCustomNotificationView(ArcNotificationItem* item) |  | 
| 235     : item_(item), |  | 
| 236       notification_key_(item->GetNotificationKey()), |  | 
| 237       event_forwarder_(new EventForwarder(this)) { |  | 
| 238   SetFocusBehavior(FocusBehavior::ALWAYS); |  | 
| 239 |  | 
| 240   item_->IncrementWindowRefCount(); |  | 
| 241   item_->AddObserver(this); |  | 
| 242 |  | 
| 243   auto* surface_manager = ArcNotificationSurfaceManager::Get(); |  | 
| 244   if (surface_manager) { |  | 
| 245     surface_manager->AddObserver(this); |  | 
| 246     exo::NotificationSurface* surface = |  | 
| 247         surface_manager->GetSurface(notification_key_); |  | 
| 248     if (surface) |  | 
| 249       OnNotificationSurfaceAdded(surface); |  | 
| 250   } |  | 
| 251 |  | 
| 252   // Create a layer as an anchor to insert surface copy during a slide. |  | 
| 253   SetPaintToLayer(); |  | 
| 254   UpdatePreferredSize(); |  | 
| 255   UpdateAccessibleName(); |  | 
| 256 } |  | 
| 257 |  | 
| 258 ArcCustomNotificationView::~ArcCustomNotificationView() { |  | 
| 259   SetSurface(nullptr); |  | 
| 260 |  | 
| 261   auto* surface_manager = ArcNotificationSurfaceManager::Get(); |  | 
| 262   if (surface_manager) |  | 
| 263     surface_manager->RemoveObserver(this); |  | 
| 264   if (item_) { |  | 
| 265     item_->RemoveObserver(this); |  | 
| 266     item_->DecrementWindowRefCount(); |  | 
| 267   } |  | 
| 268 } |  | 
| 269 |  | 
| 270 std::unique_ptr<ArcNotificationContentViewDelegate> |  | 
| 271 ArcCustomNotificationView::CreateContentViewDelegate() { |  | 
| 272   return base::MakeUnique<ArcCustomNotificationView::ContentViewDelegate>(this); |  | 
| 273 } |  | 
| 274 |  | 
| 275 void ArcCustomNotificationView::CreateCloseButton() { |  | 
| 276   DCHECK(control_buttons_view_); |  | 
| 277   DCHECK(item_); |  | 
| 278 |  | 
| 279   close_button_ = base::MakeUnique<ControlButton>(this); |  | 
| 280   close_button_->SetImage(views::CustomButton::STATE_NORMAL, |  | 
| 281                           message_center::GetCloseIcon()); |  | 
| 282   close_button_->SetAccessibleName(l10n_util::GetStringUTF16( |  | 
| 283       IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); |  | 
| 284   close_button_->SetTooltipText(l10n_util::GetStringUTF16( |  | 
| 285       IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP)); |  | 
| 286   close_button_->set_owned_by_client(); |  | 
| 287   control_buttons_view_->AddChildView(close_button_.get()); |  | 
| 288 } |  | 
| 289 |  | 
| 290 void ArcCustomNotificationView::CreateSettingsButton() { |  | 
| 291   DCHECK(control_buttons_view_); |  | 
| 292   DCHECK(item_); |  | 
| 293 |  | 
| 294   settings_button_ = new ControlButton(this); |  | 
| 295   settings_button_->SetImage(views::CustomButton::STATE_NORMAL, |  | 
| 296                              message_center::GetSettingsIcon()); |  | 
| 297   settings_button_->SetAccessibleName(l10n_util::GetStringUTF16( |  | 
| 298       IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); |  | 
| 299   settings_button_->SetTooltipText(l10n_util::GetStringUTF16( |  | 
| 300       IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); |  | 
| 301   control_buttons_view_->AddChildView(settings_button_); |  | 
| 302 } |  | 
| 303 |  | 
| 304 void ArcCustomNotificationView::MaybeCreateFloatingControlButtons() { |  | 
| 305   // Floating close button is a transient child of |surface_| and also part |  | 
| 306   // of the hosting widget's focus chain. It could only be created when both |  | 
| 307   // are present. Further, if we are being destroyed (|item_| is null), don't |  | 
| 308   // create the control buttons. |  | 
| 309   if (!surface_ || !GetWidget() || !item_) |  | 
| 310     return; |  | 
| 311 |  | 
| 312   // Creates the control_buttons_view_, which collects all control buttons into |  | 
| 313   // a horizontal box. |  | 
| 314   control_buttons_view_ = new views::View(); |  | 
| 315   control_buttons_view_->SetLayoutManager( |  | 
| 316       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); |  | 
| 317 |  | 
| 318   if (item_->IsOpeningSettingsSupported()) |  | 
| 319     CreateSettingsButton(); |  | 
| 320   if (!item_->GetPinned()) |  | 
| 321     CreateCloseButton(); |  | 
| 322 |  | 
| 323   views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); |  | 
| 324   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |  | 
| 325   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |  | 
| 326   params.parent = surface_->window(); |  | 
| 327 |  | 
| 328   floating_control_buttons_widget_.reset(new views::Widget); |  | 
| 329   floating_control_buttons_widget_->Init(params); |  | 
| 330   floating_control_buttons_widget_->SetContentsView(control_buttons_view_); |  | 
| 331 |  | 
| 332   // Put the close button into the focus chain. |  | 
| 333   floating_control_buttons_widget_->SetFocusTraversableParent( |  | 
| 334       GetWidget()->GetFocusTraversable()); |  | 
| 335   floating_control_buttons_widget_->SetFocusTraversableParentView(this); |  | 
| 336 |  | 
| 337   Layout(); |  | 
| 338 } |  | 
| 339 |  | 
| 340 void ArcCustomNotificationView::SetSurface(exo::NotificationSurface* surface) { |  | 
| 341   if (surface_ == surface) |  | 
| 342     return; |  | 
| 343 |  | 
| 344   // Reset |floating_control_buttons_widget_| when |surface_| is changed. |  | 
| 345   floating_control_buttons_widget_.reset(); |  | 
| 346   control_buttons_view_ = nullptr; |  | 
| 347   settings_button_ = nullptr; |  | 
| 348   close_button_.reset(); |  | 
| 349 |  | 
| 350   if (surface_ && surface_->window()) { |  | 
| 351     surface_->window()->RemoveObserver(this); |  | 
| 352     surface_->window()->RemovePreTargetHandler(event_forwarder_.get()); |  | 
| 353   } |  | 
| 354 |  | 
| 355   surface_ = surface; |  | 
| 356 |  | 
| 357   if (surface_ && surface_->window()) { |  | 
| 358     surface_->window()->AddObserver(this); |  | 
| 359     surface_->window()->AddPreTargetHandler(event_forwarder_.get()); |  | 
| 360 |  | 
| 361     MaybeCreateFloatingControlButtons(); |  | 
| 362 |  | 
| 363     if (GetWidget()) |  | 
| 364       AttachSurface(); |  | 
| 365   } |  | 
| 366 } |  | 
| 367 |  | 
| 368 void ArcCustomNotificationView::UpdatePreferredSize() { |  | 
| 369   gfx::Size preferred_size; |  | 
| 370   if (surface_) |  | 
| 371     preferred_size = surface_->GetSize(); |  | 
| 372   else if (item_) |  | 
| 373     preferred_size = item_->GetSnapshot().size(); |  | 
| 374 |  | 
| 375   if (preferred_size.IsEmpty()) |  | 
| 376     return; |  | 
| 377 |  | 
| 378   if (preferred_size.width() != message_center::kNotificationWidth) { |  | 
| 379     const float scale = static_cast<float>(message_center::kNotificationWidth) / |  | 
| 380                         preferred_size.width(); |  | 
| 381     preferred_size.SetSize(message_center::kNotificationWidth, |  | 
| 382                            preferred_size.height() * scale); |  | 
| 383   } |  | 
| 384 |  | 
| 385   SetPreferredSize(preferred_size); |  | 
| 386 } |  | 
| 387 |  | 
| 388 void ArcCustomNotificationView::UpdateControlButtonsVisibility() { |  | 
| 389   if (!surface_) |  | 
| 390     return; |  | 
| 391 |  | 
| 392   // TODO(edcourtney, yhanada): Creating the floating control widget here is not |  | 
| 393   // correct. This function may be called during the destruction of |  | 
| 394   // |floating_control_buttons_widget_|. This can lead to memory corruption. |  | 
| 395   // Rather than creating it here, we should fix the behaviour of OnMouseExited |  | 
| 396   // and OnMouseEntered for ARC notifications in MessageCenterView. See |  | 
| 397   // crbug.com/714587 and crbug.com/709862. |  | 
| 398   if (!floating_control_buttons_widget_) { |  | 
| 399     // This may update |floating_control_buttons_widget_|. |  | 
| 400     MaybeCreateFloatingControlButtons(); |  | 
| 401     if (!floating_control_buttons_widget_) |  | 
| 402       return; |  | 
| 403   } |  | 
| 404 |  | 
| 405   const bool target_visiblity = |  | 
| 406       IsMouseHovered() || (close_button_ && close_button_->HasFocus()) || |  | 
| 407       (settings_button_ && settings_button_->HasFocus()); |  | 
| 408   if (target_visiblity == floating_control_buttons_widget_->IsVisible()) |  | 
| 409     return; |  | 
| 410 |  | 
| 411   if (target_visiblity) |  | 
| 412     floating_control_buttons_widget_->Show(); |  | 
| 413   else |  | 
| 414     floating_control_buttons_widget_->Hide(); |  | 
| 415 } |  | 
| 416 |  | 
| 417 void ArcCustomNotificationView::UpdatePinnedState() { |  | 
| 418   if (!item_) |  | 
| 419     return; |  | 
| 420 |  | 
| 421   if (item_->GetPinned() && close_button_) { |  | 
| 422     control_buttons_view_->RemoveChildView(close_button_.get()); |  | 
| 423     close_button_.reset(); |  | 
| 424     Layout(); |  | 
| 425   } else if (!item_->GetPinned() && !close_button_) { |  | 
| 426     CreateCloseButton(); |  | 
| 427     Layout(); |  | 
| 428   } |  | 
| 429 } |  | 
| 430 |  | 
| 431 void ArcCustomNotificationView::UpdateSnapshot() { |  | 
| 432   // Bail if we have a |surface_| because it controls the sizes and paints UI. |  | 
| 433   if (surface_) |  | 
| 434     return; |  | 
| 435 |  | 
| 436   UpdatePreferredSize(); |  | 
| 437   SchedulePaint(); |  | 
| 438 } |  | 
| 439 |  | 
| 440 void ArcCustomNotificationView::AttachSurface() { |  | 
| 441   if (!GetWidget()) |  | 
| 442     return; |  | 
| 443 |  | 
| 444   UpdatePreferredSize(); |  | 
| 445   Attach(surface_->window()); |  | 
| 446 |  | 
| 447   // The texture for this window can be placed at subpixel position |  | 
| 448   // with fractional scale factor. Force to align it at the pixel |  | 
| 449   // boundary here, and when layout is updated in Layout(). |  | 
| 450   ash::wm::SnapWindowToPixelBoundary(surface_->window()); |  | 
| 451 |  | 
| 452   // Creates slide helper after this view is added to its parent. |  | 
| 453   slide_helper_.reset(new SlideHelper(this)); |  | 
| 454 |  | 
| 455   // Invokes Update() in case surface is attached during a slide. |  | 
| 456   slide_helper_->Update(); |  | 
| 457 |  | 
| 458   // Updates pinned state to create or destroy the floating close button |  | 
| 459   // after |surface_| is attached to a widget. |  | 
| 460   if (item_) |  | 
| 461     UpdatePinnedState(); |  | 
| 462 } |  | 
| 463 |  | 
| 464 void ArcCustomNotificationView::StartControlButtonsColorAnimation() { |  | 
| 465   if (control_button_color_animation_) |  | 
| 466     control_button_color_animation_->End(); |  | 
| 467   control_button_color_animation_.reset(new gfx::LinearAnimation(this)); |  | 
| 468   control_button_color_animation_->SetDuration(kBackgroundColorChangeDuration); |  | 
| 469   control_button_color_animation_->Start(); |  | 
| 470 } |  | 
| 471 |  | 
| 472 bool ArcCustomNotificationView::ShouldUpdateControlButtonsColor() const { |  | 
| 473   // Don't update the control button color when we are about to be destroyed. |  | 
| 474   if (!item_) |  | 
| 475     return false; |  | 
| 476 |  | 
| 477   if (settings_button_ && |  | 
| 478       settings_button_->background()->get_color() != |  | 
| 479           GetControlButtonBackgroundColor(item_->GetShownContents())) |  | 
| 480     return true; |  | 
| 481   if (close_button_ && |  | 
| 482       close_button_->background()->get_color() != |  | 
| 483           GetControlButtonBackgroundColor(item_->GetShownContents())) |  | 
| 484     return true; |  | 
| 485   return false; |  | 
| 486 } |  | 
| 487 |  | 
| 488 void ArcCustomNotificationView::UpdateAccessibleName() { |  | 
| 489   // Don't update the accessible name when we are about to be destroyed. |  | 
| 490   if (!item_) |  | 
| 491     return; |  | 
| 492 |  | 
| 493   accessible_name_ = item_->GetAccessibleName(); |  | 
| 494 } |  | 
| 495 |  | 
| 496 void ArcCustomNotificationView::ViewHierarchyChanged( |  | 
| 497     const views::View::ViewHierarchyChangedDetails& details) { |  | 
| 498   views::Widget* widget = GetWidget(); |  | 
| 499 |  | 
| 500   if (!details.is_add) { |  | 
| 501     // Resets slide helper when this view is removed from its parent. |  | 
| 502     slide_helper_.reset(); |  | 
| 503 |  | 
| 504     // Bail if this view is no longer attached to a widget or native_view() has |  | 
| 505     // attached to a different widget. |  | 
| 506     if (!widget || (native_view() && |  | 
| 507                     views::Widget::GetTopLevelWidgetForNativeView( |  | 
| 508                         native_view()) != widget)) { |  | 
| 509       return; |  | 
| 510     } |  | 
| 511   } |  | 
| 512 |  | 
| 513   views::NativeViewHost::ViewHierarchyChanged(details); |  | 
| 514 |  | 
| 515   if (!widget || !surface_ || !details.is_add) |  | 
| 516     return; |  | 
| 517 |  | 
| 518   AttachSurface(); |  | 
| 519 } |  | 
| 520 |  | 
| 521 void ArcCustomNotificationView::Layout() { |  | 
| 522   base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); |  | 
| 523 |  | 
| 524   views::NativeViewHost::Layout(); |  | 
| 525 |  | 
| 526   if (!surface_ || !GetWidget()) |  | 
| 527     return; |  | 
| 528 |  | 
| 529   const gfx::Rect contents_bounds = GetContentsBounds(); |  | 
| 530 |  | 
| 531   // Scale notification surface if necessary. |  | 
| 532   gfx::Transform transform; |  | 
| 533   const gfx::Size surface_size = surface_->GetSize(); |  | 
| 534   const gfx::Size contents_size = contents_bounds.size(); |  | 
| 535   if (!surface_size.IsEmpty() && !contents_size.IsEmpty()) { |  | 
| 536     transform.Scale( |  | 
| 537         static_cast<float>(contents_size.width()) / surface_size.width(), |  | 
| 538         static_cast<float>(contents_size.height()) / surface_size.height()); |  | 
| 539   } |  | 
| 540 |  | 
| 541   // Apply the transform to the surface content so that close button can |  | 
| 542   // be positioned without the need to consider the transform. |  | 
| 543   surface_->window()->children()[0]->SetTransform(transform); |  | 
| 544 |  | 
| 545   if (!floating_control_buttons_widget_) |  | 
| 546     return; |  | 
| 547 |  | 
| 548   gfx::Rect control_buttons_bounds(contents_bounds); |  | 
| 549   int buttons_width = 0; |  | 
| 550   int buttons_height = 0; |  | 
| 551   if (close_button_) { |  | 
| 552     buttons_width += close_button_->GetPreferredSize().width(); |  | 
| 553     buttons_height = close_button_->GetPreferredSize().height(); |  | 
| 554   } |  | 
| 555   if (settings_button_) { |  | 
| 556     buttons_width += settings_button_->GetPreferredSize().width(); |  | 
| 557     buttons_height = settings_button_->GetPreferredSize().height(); |  | 
| 558   } |  | 
| 559   control_buttons_bounds.set_x(control_buttons_bounds.right() - buttons_width - |  | 
| 560                                message_center::kControlButtonPadding); |  | 
| 561   control_buttons_bounds.set_y(control_buttons_bounds.y() + |  | 
| 562                                message_center::kControlButtonPadding); |  | 
| 563   control_buttons_bounds.set_width(buttons_width); |  | 
| 564   control_buttons_bounds.set_height(buttons_height); |  | 
| 565   floating_control_buttons_widget_->SetBounds(control_buttons_bounds); |  | 
| 566 |  | 
| 567   UpdateControlButtonsVisibility(); |  | 
| 568 |  | 
| 569   ash::wm::SnapWindowToPixelBoundary(surface_->window()); |  | 
| 570 } |  | 
| 571 |  | 
| 572 void ArcCustomNotificationView::OnPaint(gfx::Canvas* canvas) { |  | 
| 573   views::NativeViewHost::OnPaint(canvas); |  | 
| 574 |  | 
| 575   // Bail if there is a |surface_| or no item or no snapshot image. |  | 
| 576   if (surface_ || !item_ || item_->GetSnapshot().isNull()) |  | 
| 577     return; |  | 
| 578   const gfx::Rect contents_bounds = GetContentsBounds(); |  | 
| 579   canvas->DrawImageInt(item_->GetSnapshot(), 0, 0, item_->GetSnapshot().width(), |  | 
| 580                        item_->GetSnapshot().height(), contents_bounds.x(), |  | 
| 581                        contents_bounds.y(), contents_bounds.width(), |  | 
| 582                        contents_bounds.height(), false); |  | 
| 583 } |  | 
| 584 |  | 
| 585 void ArcCustomNotificationView::OnMouseEntered(const ui::MouseEvent&) { |  | 
| 586   UpdateControlButtonsVisibility(); |  | 
| 587 } |  | 
| 588 |  | 
| 589 void ArcCustomNotificationView::OnMouseExited(const ui::MouseEvent&) { |  | 
| 590   UpdateControlButtonsVisibility(); |  | 
| 591 } |  | 
| 592 |  | 
| 593 void ArcCustomNotificationView::OnFocus() { |  | 
| 594   CHECK_EQ(ArcNotificationView::kViewClassName, parent()->GetClassName()); |  | 
| 595 |  | 
| 596   NativeViewHost::OnFocus(); |  | 
| 597   static_cast<ArcNotificationView*>(parent())->OnContentFocused(); |  | 
| 598 } |  | 
| 599 |  | 
| 600 void ArcCustomNotificationView::OnBlur() { |  | 
| 601   if (!parent()) { |  | 
| 602     // OnBlur may be called when this view is being removed. |  | 
| 603     return; |  | 
| 604   } |  | 
| 605 |  | 
| 606   CHECK_EQ(ArcNotificationView::kViewClassName, parent()->GetClassName()); |  | 
| 607 |  | 
| 608   NativeViewHost::OnBlur(); |  | 
| 609   static_cast<ArcNotificationView*>(parent())->OnContentBlured(); |  | 
| 610 } |  | 
| 611 |  | 
| 612 void ArcCustomNotificationView::ActivateToast() { |  | 
| 613   if (message_center::ToastContentsView::kViewClassName == |  | 
| 614       parent()->parent()->GetClassName()) { |  | 
| 615     static_cast<message_center::ToastContentsView*>(parent()->parent()) |  | 
| 616         ->ActivateToast(); |  | 
| 617   } |  | 
| 618 } |  | 
| 619 |  | 
| 620 views::FocusTraversable* ArcCustomNotificationView::GetFocusTraversable() { |  | 
| 621   if (floating_control_buttons_widget_) |  | 
| 622     return static_cast<views::internal::RootView*>( |  | 
| 623         floating_control_buttons_widget_->GetRootView()); |  | 
| 624   return nullptr; |  | 
| 625 } |  | 
| 626 |  | 
| 627 bool ArcCustomNotificationView::HandleAccessibleAction( |  | 
| 628     const ui::AXActionData& action_data) { |  | 
| 629   if (item_ && action_data.action == ui::AX_ACTION_DO_DEFAULT) { |  | 
| 630     item_->ToggleExpansion(); |  | 
| 631     return true; |  | 
| 632   } |  | 
| 633   return false; |  | 
| 634 } |  | 
| 635 |  | 
| 636 void ArcCustomNotificationView::GetAccessibleNodeData( |  | 
| 637     ui::AXNodeData* node_data) { |  | 
| 638   node_data->role = ui::AX_ROLE_BUTTON; |  | 
| 639   node_data->SetName(accessible_name_); |  | 
| 640 } |  | 
| 641 |  | 
| 642 void ArcCustomNotificationView::ButtonPressed(views::Button* sender, |  | 
| 643                                               const ui::Event& event) { |  | 
| 644   if (item_ && !item_->GetPinned() && sender == close_button_.get()) { |  | 
| 645     CHECK_EQ(ArcNotificationView::kViewClassName, parent()->GetClassName()); |  | 
| 646     static_cast<ArcNotificationView*>(parent())->OnCloseButtonPressed(); |  | 
| 647   } |  | 
| 648   if (item_ && settings_button_ && sender == settings_button_) { |  | 
| 649     item_->OpenSettings(); |  | 
| 650   } |  | 
| 651 } |  | 
| 652 |  | 
| 653 void ArcCustomNotificationView::OnWindowBoundsChanged( |  | 
| 654     aura::Window* window, |  | 
| 655     const gfx::Rect& old_bounds, |  | 
| 656     const gfx::Rect& new_bounds) { |  | 
| 657   if (in_layout_) |  | 
| 658     return; |  | 
| 659 |  | 
| 660   UpdatePreferredSize(); |  | 
| 661   Layout(); |  | 
| 662 } |  | 
| 663 |  | 
| 664 void ArcCustomNotificationView::OnWindowDestroying(aura::Window* window) { |  | 
| 665   SetSurface(nullptr); |  | 
| 666 } |  | 
| 667 |  | 
| 668 void ArcCustomNotificationView::OnItemDestroying() { |  | 
| 669   item_->RemoveObserver(this); |  | 
| 670   item_ = nullptr; |  | 
| 671 |  | 
| 672   // Reset |surface_| with |item_| since no one is observing the |surface_| |  | 
| 673   // after |item_| is gone and this view should be removed soon. |  | 
| 674   SetSurface(nullptr); |  | 
| 675 } |  | 
| 676 |  | 
| 677 void ArcCustomNotificationView::OnItemUpdated() { |  | 
| 678   UpdateAccessibleName(); |  | 
| 679   UpdatePinnedState(); |  | 
| 680   UpdateSnapshot(); |  | 
| 681   if (ShouldUpdateControlButtonsColor()) |  | 
| 682     StartControlButtonsColorAnimation(); |  | 
| 683 } |  | 
| 684 |  | 
| 685 void ArcCustomNotificationView::OnNotificationSurfaceAdded( |  | 
| 686     exo::NotificationSurface* surface) { |  | 
| 687   if (surface->notification_id() != notification_key_) |  | 
| 688     return; |  | 
| 689 |  | 
| 690   SetSurface(surface); |  | 
| 691 } |  | 
| 692 |  | 
| 693 void ArcCustomNotificationView::OnNotificationSurfaceRemoved( |  | 
| 694     exo::NotificationSurface* surface) { |  | 
| 695   if (surface->notification_id() != notification_key_) |  | 
| 696     return; |  | 
| 697 |  | 
| 698   SetSurface(nullptr); |  | 
| 699 } |  | 
| 700 |  | 
| 701 void ArcCustomNotificationView::AnimationEnded( |  | 
| 702     const gfx::Animation* animation) { |  | 
| 703   DCHECK_EQ(animation, control_button_color_animation_.get()); |  | 
| 704   control_button_color_animation_.reset(); |  | 
| 705 } |  | 
| 706 |  | 
| 707 void ArcCustomNotificationView::AnimationProgressed( |  | 
| 708     const gfx::Animation* animation) { |  | 
| 709   DCHECK_EQ(animation, control_button_color_animation_.get()); |  | 
| 710 |  | 
| 711   if (item_) { |  | 
| 712     const SkColor target = |  | 
| 713         GetControlButtonBackgroundColor(item_->GetShownContents()); |  | 
| 714     const SkColor start = |  | 
| 715         target == message_center::kControlButtonBackgroundColor |  | 
| 716             ? SK_ColorTRANSPARENT |  | 
| 717             : message_center::kControlButtonBackgroundColor; |  | 
| 718     const SkColor current_color = gfx::Tween::ColorValueBetween( |  | 
| 719         animation->GetCurrentValue(), start, target); |  | 
| 720     if (settings_button_) { |  | 
| 721       settings_button_->set_background( |  | 
| 722           views::Background::CreateSolidBackground(current_color)); |  | 
| 723       settings_button_->SchedulePaint(); |  | 
| 724     } |  | 
| 725     if (close_button_) { |  | 
| 726       close_button_->set_background( |  | 
| 727           views::Background::CreateSolidBackground(current_color)); |  | 
| 728       close_button_->SchedulePaint(); |  | 
| 729     } |  | 
| 730   } |  | 
| 731 } |  | 
| 732 |  | 
| 733 }  // namespace arc |  | 
| OLD | NEW | 
|---|