1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/ui/idle_app_name_notification_view.h"
10 #include "ash/shell_delegate.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/wm/window_animations.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "base/timer/timer.h"
17 #include "extensions/common/extension.h"
18 #include "grit/generated_resources.h"
19 #include "ui/accessibility/ax_view_state.h"
20 #include "ui/aura/window.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/compositor/layer_animation_observer.h"
24 #include "ui/compositor/scoped_layer_animation_settings.h"
25 #include "ui/gfx/canvas.h"
26 #include "ui/gfx/font_list.h"
27 #include "ui/gfx/text_utils.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/layout/fill_layout.h"
31 #include "ui/views/view.h"
32 #include "ui/views/widget/widget.h"
33 #include "ui/views/widget/widget_delegate.h"
36 class LayerAnimationSequence
;
42 // Color of the text of the warning message.
43 const SkColor kTextColor
= SK_ColorBLACK
;
45 // Color of the text of the warning message.
46 const SkColor kErrorTextColor
= SK_ColorRED
;
48 // Color of the window background.
49 const SkColor kWindowBackgroundColor
= SK_ColorWHITE
;
51 // Radius of the rounded corners of the window.
52 const int kWindowCornerRadius
= 4;
54 // Creates and shows the message widget for |view| with |animation_time_ms|.
55 void CreateAndShowWidgetWithContent(views::WidgetDelegate
* delegate
,
57 int animation_time_ms
) {
58 aura::Window
* root_window
= ash::Shell::GetTargetRootWindow();
59 gfx::Size rs
= root_window
->bounds().size();
60 gfx::Size ps
= view
->GetPreferredSize();
61 gfx::Rect
bounds((rs
.width() - ps
.width()) / 2,
65 views::Widget::InitParams params
;
66 params
.type
= views::Widget::InitParams::TYPE_POPUP
;
67 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
68 params
.ownership
= views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET
;
69 params
.accept_events
= false;
70 params
.can_activate
= false;
71 params
.keep_on_top
= true;
72 params
.remove_standard_frame
= true;
73 params
.delegate
= delegate
;
74 params
.bounds
= bounds
;
75 params
.parent
= ash::Shell::GetContainer(
76 root_window
, ash::kShellWindowId_SettingBubbleContainer
);
77 views::Widget
* widget
= new views::Widget
;
79 widget
->SetContentsView(view
);
80 gfx::NativeView native_view
= widget
->GetNativeView();
81 native_view
->SetName("KioskIdleAppNameNotification");
83 // Note: We cannot use the Window show/hide animations since they are disabled
84 // for kiosk by command line.
85 ui::LayerAnimator
* animator
= new ui::LayerAnimator(
86 base::TimeDelta::FromMilliseconds(animation_time_ms
));
87 native_view
->layer()->SetAnimator(animator
);
90 // We don't care about the show animation since it is off screen, so stop the
91 // started animation and move the message into view.
92 animator
->StopAnimating();
93 bounds
.set_y((rs
.height() - ps
.height()) / 20);
94 widget
->SetBounds(bounds
);
96 // Allow to use the message for spoken feedback.
97 view
->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, true);
102 // The class which implements the content view for the message.
103 class IdleAppNameNotificationDelegateView
104 : public views::WidgetDelegateView
,
105 public ui::ImplicitAnimationObserver
{
107 // An idle message which will get shown from the caller and hides itself after
108 // a time, calling |owner->CloseMessage| to inform the owner that it got
109 // destroyed. The |app_name| is a string which gets used as message and
110 // |error| is true if something is not correct.
111 // |message_visibility_time_in_ms| ms's after creation the message will start
112 // to remove itself from the screen.
113 IdleAppNameNotificationDelegateView(IdleAppNameNotificationView
*owner
,
114 const base::string16
& app_name
,
116 int message_visibility_time_in_ms
)
118 widget_closed_(false) {
119 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
120 // Add the application name label to the message.
122 rb
.GetFontList(ui::ResourceBundle::BoldFont
),
123 error
? kErrorTextColor
: kTextColor
);
124 spoken_text_
= app_name
;
125 SetLayoutManager(new views::FillLayout
);
127 // Set a timer which will trigger to remove the message after the given
131 base::TimeDelta::FromMilliseconds(message_visibility_time_in_ms
),
133 &IdleAppNameNotificationDelegateView::RemoveMessage
);
136 virtual ~IdleAppNameNotificationDelegateView() {
137 // The widget is already closing, but the other cleanup items need to be
139 widget_closed_
= true;
143 // Close the widget immediately. This can be called from the owner or from
146 // Stop the timer (if it was running).
148 // Inform our owner that we are going away.
150 IdleAppNameNotificationView
* owner
= owner_
;
152 owner
->CloseMessage();
154 // Close the owning widget - if required.
155 if (!widget_closed_
) {
156 widget_closed_
= true;
157 GetWidget()->Close();
161 // Animate the window away (and close once done).
162 void RemoveMessage() {
163 aura::Window
* widget_view
= GetWidget()->GetNativeView();
164 ui::Layer
* layer
= widget_view
->layer();
165 ui::ScopedLayerAnimationSettings
settings(layer
->GetAnimator());
166 settings
.AddObserver(this);
167 gfx::Rect rect
= widget_view
->bounds();
168 rect
.set_y(-GetPreferredSize().height());
169 layer
->SetBounds(rect
);
172 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
{
174 paint
.setStyle(SkPaint::kFill_Style
);
175 paint
.setColor(kWindowBackgroundColor
);
176 canvas
->DrawRoundRect(GetLocalBounds(), kWindowCornerRadius
, paint
);
177 views::WidgetDelegateView::OnPaint(canvas
);
180 virtual void GetAccessibleState(ui::AXViewState
* state
) OVERRIDE
{
181 state
->name
= spoken_text_
;
182 state
->role
= ui::AX_ROLE_ALERT
;
185 // ImplicitAnimationObserver overrides
186 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
191 // Adds the label to the view, using |text| with a |font| and a |text_color|.
192 void AddLabel(const base::string16
& text
,
193 const gfx::FontList
& font
,
194 SkColor text_color
) {
195 views::Label
* label
= new views::Label
;
196 label
->SetText(text
);
197 label
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
198 label
->SetFontList(font
);
199 label
->SetEnabledColor(text_color
);
200 label
->SetDisabledColor(text_color
);
201 label
->SetAutoColorReadabilityEnabled(false);
205 // A timer which calls us to remove the message from the screen.
206 base::OneShotTimer
<IdleAppNameNotificationDelegateView
> hide_timer_
;
208 // The owner of this message which needs to get notified when the message
210 IdleAppNameNotificationView
* owner_
;
213 base::string16 spoken_text_
;
215 // True if the widget got already closed.
218 DISALLOW_COPY_AND_ASSIGN(IdleAppNameNotificationDelegateView
);
221 IdleAppNameNotificationView::IdleAppNameNotificationView(
222 int message_visibility_time_in_ms
,
223 int animation_time_ms
,
224 const extensions::Extension
* extension
)
226 ShowMessage(message_visibility_time_in_ms
, animation_time_ms
, extension
);
229 IdleAppNameNotificationView::~IdleAppNameNotificationView() {
233 void IdleAppNameNotificationView::CloseMessage() {
235 IdleAppNameNotificationDelegateView
* view
= view_
;
241 bool IdleAppNameNotificationView::IsVisible() {
242 return view_
!= NULL
;
245 base::string16
IdleAppNameNotificationView::GetShownTextForTest() {
246 ui::AXViewState state
;
248 view_
->GetAccessibleState(&state
);
252 void IdleAppNameNotificationView::ShowMessage(
253 int message_visibility_time_in_ms
,
254 int animation_time_ms
,
255 const extensions::Extension
* extension
) {
258 base::string16 app_name
;
261 !base::ContainsOnlyChars(extension
->name(), base::kWhitespaceASCII
)) {
262 app_name
= base::UTF8ToUTF16(extension
->name());
265 app_name
= l10n_util::GetStringUTF16(
266 IDS_IDLE_APP_NAME_UNKNOWN_APPLICATION_NOTIFICATION
);
269 view_
= new IdleAppNameNotificationDelegateView(
273 message_visibility_time_in_ms
+ animation_time_ms
);
274 CreateAndShowWidgetWithContent(view_
, view_
, animation_time_ms
);
277 } // namespace chromeos