• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
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 // Creates and shows the message widget for |view| with |animation_time_ms|.
CreateAndShowWidgetWithContent(views::WidgetDelegate * delegate,views::View * view,int animation_time_ms)55 void CreateAndShowWidgetWithContent(views::WidgetDelegate* delegate,
56                                     views::View* view,
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,
62                    -ps.height(),
63                    ps.width(),
64                    ps.height());
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.keep_on_top = true;
71   params.remove_standard_frame = true;
72   params.delegate = delegate;
73   params.bounds = bounds;
74   params.parent = ash::Shell::GetContainer(
75       root_window, ash::kShellWindowId_SettingBubbleContainer);
76   views::Widget* widget = new views::Widget;
77   widget->Init(params);
78   widget->SetContentsView(view);
79   gfx::NativeView native_view = widget->GetNativeView();
80   native_view->SetName("KioskIdleAppNameNotification");
81 
82   // Note: We cannot use the Window show/hide animations since they are disabled
83   // for kiosk by command line.
84   ui::LayerAnimator* animator = new ui::LayerAnimator(
85           base::TimeDelta::FromMilliseconds(animation_time_ms));
86   native_view->layer()->SetAnimator(animator);
87   widget->Show();
88 
89   // We don't care about the show animation since it is off screen, so stop the
90   // started animation and move the message into view.
91   animator->StopAnimating();
92   bounds.set_y((rs.height() - ps.height()) / 20);
93   widget->SetBounds(bounds);
94 
95   // Allow to use the message for spoken feedback.
96   view->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
97 }
98 
99 }  // namespace
100 
101 // The class which implements the content view for the message.
102 class IdleAppNameNotificationDelegateView
103     : public views::WidgetDelegateView,
104       public ui::ImplicitAnimationObserver {
105  public:
106   // An idle message which will get shown from the caller and hides itself after
107   // a time, calling |owner->CloseMessage| to inform the owner that it got
108   // destroyed. The |app_name| is a string which gets used as message and
109   // |error| is true if something is not correct.
110   // |message_visibility_time_in_ms| ms's after creation the message will start
111   // to remove itself from the screen.
IdleAppNameNotificationDelegateView(IdleAppNameNotificationView * owner,const base::string16 & app_name,bool error,int message_visibility_time_in_ms)112   IdleAppNameNotificationDelegateView(IdleAppNameNotificationView *owner,
113                                       const base::string16& app_name,
114                                       bool error,
115                                       int message_visibility_time_in_ms)
116       : owner_(owner),
117         widget_closed_(false) {
118     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
119     // Add the application name label to the message.
120     AddLabel(app_name,
121              rb.GetFontList(ui::ResourceBundle::BoldFont),
122              error ? kErrorTextColor : kTextColor);
123     spoken_text_ = app_name;
124     SetLayoutManager(new views::FillLayout);
125 
126     // Set a timer which will trigger to remove the message after the given
127     // time.
128     hide_timer_.Start(
129         FROM_HERE,
130         base::TimeDelta::FromMilliseconds(message_visibility_time_in_ms),
131         this,
132         &IdleAppNameNotificationDelegateView::RemoveMessage);
133   }
134 
~IdleAppNameNotificationDelegateView()135   virtual ~IdleAppNameNotificationDelegateView() {
136     // The widget is already closing, but the other cleanup items need to be
137     // performed.
138     widget_closed_ = true;
139     Close();
140   }
141 
142   // Close the widget immediately. This can be called from the owner or from
143   // this class.
Close()144   void Close() {
145     // Stop the timer (if it was running).
146     hide_timer_.Stop();
147     // Inform our owner that we are going away.
148     if (owner_) {
149       IdleAppNameNotificationView* owner = owner_;
150       owner_ = NULL;
151       owner->CloseMessage();
152     }
153     // Close the owning widget - if required.
154     if (!widget_closed_) {
155       widget_closed_ = true;
156       GetWidget()->Close();
157     }
158   }
159 
160   // Animate the window away (and close once done).
RemoveMessage()161   void RemoveMessage() {
162     aura::Window* widget_view = GetWidget()->GetNativeView();
163     ui::Layer* layer = widget_view->layer();
164     ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
165     settings.AddObserver(this);
166     gfx::Rect rect = widget_view->bounds();
167     rect.set_y(-GetPreferredSize().height());
168     layer->SetBounds(rect);
169   }
170 
OnPaint(gfx::Canvas * canvas)171   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
172     SkPaint paint;
173     paint.setStyle(SkPaint::kFill_Style);
174     paint.setColor(kWindowBackgroundColor);
175     canvas->DrawRoundRect(GetLocalBounds(), kWindowCornerRadius, paint);
176     views::WidgetDelegateView::OnPaint(canvas);
177   }
178 
GetAccessibleState(ui::AXViewState * state)179   virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
180     state->name = spoken_text_;
181     state->role = ui::AX_ROLE_ALERT;
182   }
183 
184   // ImplicitAnimationObserver overrides
OnImplicitAnimationsCompleted()185   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
186     Close();
187   }
188 
189  private:
190   // Adds the label to the view, using |text| with a |font| and a |text_color|.
AddLabel(const base::string16 & text,const gfx::FontList & font,SkColor text_color)191   void AddLabel(const base::string16& text,
192                 const gfx::FontList& font,
193                 SkColor text_color) {
194     views::Label* label = new views::Label;
195     label->SetText(text);
196     label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
197     label->SetFontList(font);
198     label->SetEnabledColor(text_color);
199     label->SetDisabledColor(text_color);
200     label->SetAutoColorReadabilityEnabled(false);
201     AddChildView(label);
202   }
203 
204   // A timer which calls us to remove the message from the screen.
205   base::OneShotTimer<IdleAppNameNotificationDelegateView> hide_timer_;
206 
207   // The owner of this message which needs to get notified when the message
208   // closes.
209   IdleAppNameNotificationView* owner_;
210 
211   // The spoken text.
212   base::string16 spoken_text_;
213 
214   // True if the widget got already closed.
215   bool widget_closed_;
216 
217   DISALLOW_COPY_AND_ASSIGN(IdleAppNameNotificationDelegateView);
218 };
219 
IdleAppNameNotificationView(int message_visibility_time_in_ms,int animation_time_ms,const extensions::Extension * extension)220 IdleAppNameNotificationView::IdleAppNameNotificationView(
221     int message_visibility_time_in_ms,
222     int animation_time_ms,
223     const extensions::Extension* extension)
224     : view_(NULL) {
225   ShowMessage(message_visibility_time_in_ms, animation_time_ms, extension);
226 }
227 
~IdleAppNameNotificationView()228 IdleAppNameNotificationView::~IdleAppNameNotificationView() {
229   CloseMessage();
230 }
231 
CloseMessage()232 void IdleAppNameNotificationView::CloseMessage() {
233   if (view_) {
234     IdleAppNameNotificationDelegateView* view = view_;
235     view_ = NULL;
236     view->Close();
237   }
238 }
239 
IsVisible()240 bool IdleAppNameNotificationView::IsVisible() {
241   return view_ != NULL;
242 }
243 
GetShownTextForTest()244 base::string16 IdleAppNameNotificationView::GetShownTextForTest() {
245   ui::AXViewState state;
246   DCHECK(view_);
247   view_->GetAccessibleState(&state);
248   return state.name;
249 }
250 
ShowMessage(int message_visibility_time_in_ms,int animation_time_ms,const extensions::Extension * extension)251 void IdleAppNameNotificationView::ShowMessage(
252     int message_visibility_time_in_ms,
253     int animation_time_ms,
254     const extensions::Extension* extension) {
255   DCHECK(!view_);
256 
257   base::string16 app_name;
258   bool error = false;
259   if (extension &&
260       !base::ContainsOnlyChars(extension->name(), base::kWhitespaceASCII)) {
261     app_name = base::UTF8ToUTF16(extension->name());
262   } else {
263     error = true;
264     app_name = l10n_util::GetStringUTF16(
265         IDS_IDLE_APP_NAME_UNKNOWN_APPLICATION_NOTIFICATION);
266   }
267 
268   view_ = new IdleAppNameNotificationDelegateView(
269       this,
270       app_name,
271       error,
272       message_visibility_time_in_ms + animation_time_ms);
273   CreateAndShowWidgetWithContent(view_, view_, animation_time_ms);
274 }
275 
276 }  // namespace chromeos
277