Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / ui / idle_app_name_notification_view.cc
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.
4
5 #include "chrome/browser/chromeos/ui/idle_app_name_notification_view.h"
6
7 #include <string>
8
9 #include "ash/shell.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/aura/window.h"
20 #include "ui/base/accessibility/accessible_view_state.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"
34
35 namespace ui {
36 class LayerAnimationSequence;
37 }
38
39 namespace chromeos {
40 namespace {
41
42 // Color of the text of the warning message.
43 const SkColor kTextColor = SK_ColorBLACK;
44
45 // Color of the text of the warning message.
46 const SkColor kErrorTextColor = SK_ColorRED;
47
48 // Color of the window background.
49 const SkColor kWindowBackgroundColor = SK_ColorWHITE;
50
51 // Radius of the rounded corners of the window.
52 const int kWindowCornerRadius = 4;
53
54 // Spacing around the text.
55 const int kHorizontalMarginAroundText = 50;
56 const int kVerticalMarginAroundText = 25;
57 const int kVerticalSpacingBetweenText = 10;
58
59 // Creates and shows the message widget for |view| with |animation_time_ms|.
60 void CreateAndShowWidgetWithContent(views::WidgetDelegate* delegate,
61                                     views::View* view,
62                                     int animation_time_ms) {
63   aura::Window* root_window = ash::Shell::GetTargetRootWindow();
64   gfx::Size rs = root_window->bounds().size();
65   gfx::Size ps = view->GetPreferredSize();
66   gfx::Rect bounds((rs.width() - ps.width()) / 2,
67                    -ps.height(),
68                    ps.width(),
69                    ps.height());
70   views::Widget::InitParams params;
71   params.type = views::Widget::InitParams::TYPE_POPUP;
72   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
73   params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
74   params.accept_events = false;
75   params.can_activate = false;
76   params.keep_on_top = true;
77   params.remove_standard_frame = true;
78   params.delegate = delegate;
79   params.bounds = bounds;
80   params.parent = ash::Shell::GetContainer(
81       root_window,
82       ash::internal::kShellWindowId_SettingBubbleContainer);
83   views::Widget* widget = new views::Widget;
84   widget->Init(params);
85   widget->SetContentsView(view);
86   gfx::NativeView native_view = widget->GetNativeView();
87   native_view->SetName("KioskIdleAppNameNotification");
88
89   // Note: We cannot use the Window show/hide animations since they are disabled
90   // for kiosk by command line.
91   ui::LayerAnimator* animator = new ui::LayerAnimator(
92           base::TimeDelta::FromMilliseconds(animation_time_ms));
93   native_view->layer()->SetAnimator(animator);
94   widget->Show();
95
96   // We don't care about the show animation since it is off screen, so stop the
97   // started animation and move the message into view.
98   animator->StopAnimating();
99   bounds.set_y((rs.height() - ps.height()) / 20);
100   widget->SetBounds(bounds);
101
102   // Allow to use the message for spoken feedback.
103   view->NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
104 }
105
106 }  // namespace
107
108 // The class which implements the content view for the message.
109 class IdleAppNameNotificationDelegateView
110     : public views::WidgetDelegateView,
111       public ui::ImplicitAnimationObserver {
112  public:
113   // An idle message which will get shown from the caller and hides itself after
114   // a time, calling |owner->CloseMessage| to inform the owner that it got
115   // destroyed. The |app_name| and the |app_author| are strings which get
116   // used as message and |error| is true if something is not correct.
117   // |message_visibility_time_in_ms| ms's after creation the message will start
118   // to remove itself from the screen.
119   IdleAppNameNotificationDelegateView(IdleAppNameNotificationView *owner,
120                                       const base::string16& app_name,
121                                       const base::string16& app_author,
122                                       bool error,
123                                       int message_visibility_time_in_ms)
124       : owner_(owner),
125         widget_closed_(false) {
126     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
127     // Add the application name label to the message.
128     AddLabel(app_name,
129              rb.GetFontList(ui::ResourceBundle::BoldFont),
130              error && app_author.empty() ? kErrorTextColor : kTextColor);
131     if (!app_author.empty()) {
132       // Add the author label to the message.
133       base::string16 app_by_author =
134             l10n_util::GetStringFUTF16(IDS_IDLE_APP_NAME_NOTIFICATION,
135                                        app_author);
136       AddLabel(app_by_author,
137                rb.GetFontList(ui::ResourceBundle::BaseFont),
138                error ? kErrorTextColor : kTextColor);
139       spoken_text_ =
140           l10n_util::GetStringFUTF16(IDS_IDLE_APP_NAME_SPOKEN_NOTIFICATION,
141                                      app_name,
142                                      app_by_author);
143       SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
144                                             kHorizontalMarginAroundText,
145                                             kVerticalMarginAroundText,
146                                             kVerticalSpacingBetweenText));
147     } else {
148       spoken_text_ = app_name;
149       SetLayoutManager(new views::FillLayout);
150     }
151
152     // Set a timer which will trigger to remove the message after the given
153     // time.
154     hide_timer_.Start(
155         FROM_HERE,
156         base::TimeDelta::FromMilliseconds(message_visibility_time_in_ms),
157         this,
158         &IdleAppNameNotificationDelegateView::RemoveMessage);
159   }
160
161   virtual ~IdleAppNameNotificationDelegateView() {
162     // The widget is already closing, but the other cleanup items need to be
163     // performed.
164     widget_closed_ = true;
165     Close();
166   }
167
168   // Close the widget immediately. This can be called from the owner or from
169   // this class.
170   void Close() {
171     // Stop the timer (if it was running).
172     hide_timer_.Stop();
173     // Inform our owner that we are going away.
174     if (owner_) {
175       IdleAppNameNotificationView* owner = owner_;
176       owner_ = NULL;
177       owner->CloseMessage();
178     }
179     // Close the owning widget - if required.
180     if (!widget_closed_) {
181       widget_closed_ = true;
182       GetWidget()->Close();
183     }
184   }
185
186   // Animate the window away (and close once done).
187   void RemoveMessage() {
188     aura::Window* widget_view = GetWidget()->GetNativeView();
189     ui::Layer* layer = widget_view->layer();
190     ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
191     settings.AddObserver(this);
192     gfx::Rect rect = widget_view->bounds();
193     rect.set_y(-GetPreferredSize().height());
194     layer->SetBounds(rect);
195   }
196
197   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
198     SkPaint paint;
199     paint.setStyle(SkPaint::kFill_Style);
200     paint.setColor(kWindowBackgroundColor);
201     canvas->DrawRoundRect(GetLocalBounds(), kWindowCornerRadius, paint);
202     views::WidgetDelegateView::OnPaint(canvas);
203   }
204
205   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
206     state->name = spoken_text_;
207     state->role = ui::AccessibilityTypes::ROLE_ALERT;
208   }
209
210   // ImplicitAnimationObserver overrides
211   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
212     Close();
213   }
214
215  private:
216   // Adds the label to the view, using |text| with a |font| and a |text_color|.
217   void AddLabel(const base::string16& text,
218                 const gfx::FontList& font,
219                 SkColor text_color) {
220     views::Label* label = new views::Label;
221     label->SetText(text);
222     label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
223     label->SetFontList(font);
224     label->SetEnabledColor(text_color);
225     label->SetDisabledColor(text_color);
226     label->SetAutoColorReadabilityEnabled(false);
227     AddChildView(label);
228   }
229
230   // A timer which calls us to remove the message from the screen.
231   base::OneShotTimer<IdleAppNameNotificationDelegateView> hide_timer_;
232
233   // The owner of this message which needs to get notified when the message
234   // closes.
235   IdleAppNameNotificationView* owner_;
236
237   // The spoken text.
238   base::string16 spoken_text_;
239
240   // True if the widget got already closed.
241   bool widget_closed_;
242
243   DISALLOW_COPY_AND_ASSIGN(IdleAppNameNotificationDelegateView);
244 };
245
246 IdleAppNameNotificationView::IdleAppNameNotificationView(
247     int message_visibility_time_in_ms,
248     int animation_time_ms,
249     const extensions::Extension* extension)
250     : view_(NULL) {
251   ShowMessage(message_visibility_time_in_ms, animation_time_ms, extension);
252 }
253
254 IdleAppNameNotificationView::~IdleAppNameNotificationView() {
255   CloseMessage();
256 }
257
258 void IdleAppNameNotificationView::CloseMessage() {
259   if (view_) {
260     IdleAppNameNotificationDelegateView* view = view_;
261     view_ = NULL;
262     view->Close();
263   }
264 }
265
266 bool IdleAppNameNotificationView::IsVisible() {
267   return view_ != NULL;
268 }
269
270 base::string16 IdleAppNameNotificationView::GetShownTextForTest() {
271   ui::AccessibleViewState state;
272   DCHECK(view_);
273   view_->GetAccessibleState(&state);
274   return state.name;
275 }
276
277 void IdleAppNameNotificationView::ShowMessage(
278     int message_visibility_time_in_ms,
279     int animation_time_ms,
280     const extensions::Extension* extension) {
281   DCHECK(!view_);
282
283   base::string16 author;
284   base::string16 app_name;
285   bool error = false;
286   if (extension && !ContainsOnlyWhitespaceASCII(extension->name())) {
287     app_name = base::UTF8ToUTF16(extension->name());
288     // TODO(skuhne): This might not be enough since the author flag is not
289     // explicitly enforced by us, but for Kiosk mode we could maybe require it.
290     extension->manifest()->GetString("author", &author);
291     if (ContainsOnlyWhitespace(author)) {
292       error = true;
293       author = l10n_util::GetStringUTF16(
294           IDS_IDLE_APP_NAME_INVALID_AUTHOR_NOTIFICATION);
295     }
296   } else {
297     error = true;
298     app_name = l10n_util::GetStringUTF16(
299         IDS_IDLE_APP_NAME_UNKNOWN_APPLICATION_NOTIFICATION);
300   }
301
302   view_ = new IdleAppNameNotificationDelegateView(
303       this,
304       app_name,
305       author,
306       error,
307       message_visibility_time_in_ms + animation_time_ms);
308   CreateAndShowWidgetWithContent(view_, view_, animation_time_ms);
309 }
310
311 }  // namespace chromeos