Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc

Issue 2633423004: Displays the calibration complete message to touch calibration and gracefully quits (Closed)
Patch Set: Small addition Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view .h" 5 #include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view .h"
6 6
7 #include "ash/display/window_tree_host_manager.h" 7 #include "ash/display/window_tree_host_manager.h"
8 #include "ash/public/cpp/shell_window_ids.h" 8 #include "ash/public/cpp/shell_window_ids.h"
9 #include "ash/resources/vector_icons/vector_icons.h"
9 #include "ash/shell.h" 10 #include "ash/shell.h"
10 #include "ui/aura/window.h" 11 #include "ui/aura/window.h"
11 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/base/resource/resource_bundle.h"
12 #include "ui/compositor/scoped_layer_animation_settings.h" 13 #include "ui/compositor/scoped_layer_animation_settings.h"
13 #include "ui/gfx/animation/linear_animation.h" 14 #include "ui/gfx/animation/linear_animation.h"
14 #include "ui/gfx/animation/throb_animation.h" 15 #include "ui/gfx/animation/throb_animation.h"
15 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/paint_vector_icon.h"
16 #include "ui/strings/grit/ui_strings.h" 18 #include "ui/strings/grit/ui_strings.h"
17 #include "ui/views/background.h" 19 #include "ui/views/background.h"
18 #include "ui/views/controls/label.h" 20 #include "ui/views/controls/label.h"
19 #include "ui/views/widget/widget.h" 21 #include "ui/views/widget/widget.h"
20 22
21 namespace chromeos { 23 namespace chromeos {
22 24
23 namespace { 25 namespace {
24 26
25 constexpr char kWidgetName[] = "TouchCalibratorOverlay"; 27 constexpr char kWidgetName[] = "TouchCalibratorOverlay";
26 28
27 constexpr int kAnimationFrameRate = 100; 29 constexpr int kAnimationFrameRate = 100;
28 constexpr int kFadeDurationInMs = 150; 30 constexpr int kFadeDurationInMs = 150;
29 constexpr int kPointMoveDurationInMs = 600; 31 constexpr int kPointMoveDurationInMs = 400;
30 constexpr int kPointMoveDurationLongInMs = 700; 32 constexpr int kPointMoveDurationLongInMs = 500;
31 33
32 const SkColor kExitLabelColor = SkColorSetARGBInline(255, 96, 96, 96); 34 const SkColor kExitLabelColor = SkColorSetARGBInline(255, 96, 96, 96);
33 const SkColor kExitLabelShadowColor = SkColorSetARGBInline(255, 11, 11, 11); 35 const SkColor kExitLabelShadowColor = SkColorSetARGBInline(255, 11, 11, 11);
34 constexpr int kExitLabelWidth = 300; 36 constexpr int kExitLabelWidth = 300;
35 constexpr int kExitLabelHeight = 20; 37 constexpr int kExitLabelHeight = 20;
36 38
37 const SkColor kTapHereLabelColor = SK_ColorWHITE; 39 const SkColor kTapHereLabelColor = SK_ColorWHITE;
38 40
39 constexpr int kHintBoxWidth = 298; 41 constexpr int kHintBoxWidth = 298;
40 constexpr int kHintBoxHeight = 180; 42 constexpr int kHintBoxHeight = 180;
41 constexpr int kHintBoxLabelTextSize = 5; 43 constexpr int kHintBoxLabelTextSize = 5;
42 constexpr int kHintBoxSublabelTextSize = 3; 44 constexpr int kHintBoxSublabelTextSize = 3;
43 45
44 constexpr int kThrobberCircleViewWidth = 64; 46 constexpr int kThrobberCircleViewWidth = 64;
45 constexpr float kThrobberCircleRadiusFactor = 3.f / 8.f; 47 constexpr float kThrobberCircleRadiusFactor = 3.f / 8.f;
46 48
49 constexpr int kFinalMessageTransitionDurationMs = 200;
50 constexpr int kCompleteMessageViewWidth = 427;
51 constexpr int kCompleteMessageViewHeight = kThrobberCircleViewWidth;
52 constexpr int kCompleteMessageTextSize = 16;
53
47 constexpr int kTouchPointViewOffset = 100; 54 constexpr int kTouchPointViewOffset = 100;
48 55
49 constexpr int kTapLabelHeight = 48; 56 constexpr int kTapLabelHeight = 48;
50 constexpr int kTapLabelWidth = 80; 57 constexpr int kTapLabelWidth = 80;
51 58
52 const SkColor kHintLabelTextColor = SK_ColorBLACK; 59 const SkColor kHintLabelTextColor = SK_ColorBLACK;
53 const SkColor kHintSublabelTextColor = SkColorSetARGBInline(255, 161, 161, 161); 60 const SkColor kHintSublabelTextColor = SkColorSetARGBInline(255, 161, 161, 161);
54 61
55 const SkColor kInnerCircleColor = SK_ColorWHITE; 62 const SkColor kInnerCircleColor = SK_ColorWHITE;
56 const SkColor kOuterCircleColor = SkColorSetA(kInnerCircleColor, 128); 63 const SkColor kOuterCircleColor = SkColorSetA(kInnerCircleColor, 255 * 0.2);
57 64
58 constexpr int kCircleAnimationDurationMs = 900; 65 constexpr int kCircleAnimationDurationMs = 900;
59 66
60 constexpr int kHintRectBorderRadius = 4; 67 constexpr int kHintRectBorderRadius = 4;
61 68
62 constexpr float kBackgroundFinalOpacity = 0.75f; 69 constexpr float kBackgroundFinalOpacity = 0.75f;
63 70
64 // Returns the initialization params for the widget that contains the touch 71 // Returns the initialization params for the widget that contains the touch
65 // calibrator view. 72 // calibrator view.
66 views::Widget::InitParams GetWidgetParams(aura::Window* root_window) { 73 views::Widget::InitParams GetWidgetParams(aura::Window* root_window) {
(...skipping 13 matching lines...) Expand all
80 // Returns the size of bounding box required for |text| of given |font_list|. 87 // Returns the size of bounding box required for |text| of given |font_list|.
81 gfx::Size GetSizeForString(const base::string16& text, 88 gfx::Size GetSizeForString(const base::string16& text,
82 const gfx::FontList& font_list) { 89 const gfx::FontList& font_list) {
83 int height = 0, width = 0; 90 int height = 0, width = 0;
84 gfx::Canvas::SizeStringInt(text, font_list, &width, &height, 0, 0); 91 gfx::Canvas::SizeStringInt(text, font_list, &width, &height, 0, 0);
85 return gfx::Size(width, height); 92 return gfx::Size(width, height);
86 } 93 }
87 94
88 void AnimateLayerToPosition(views::View* view, 95 void AnimateLayerToPosition(views::View* view,
89 int duration, 96 int duration,
90 gfx::Point end_position) { 97 gfx::Point end_position,
98 float opacity = 1.f) {
91 ui::ScopedLayerAnimationSettings slide_settings(view->layer()->GetAnimator()); 99 ui::ScopedLayerAnimationSettings slide_settings(view->layer()->GetAnimator());
92 slide_settings.SetPreemptionStrategy( 100 slide_settings.SetPreemptionStrategy(
93 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 101 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
94 slide_settings.SetTransitionDuration( 102 slide_settings.SetTransitionDuration(
95 base::TimeDelta::FromMilliseconds(duration)); 103 base::TimeDelta::FromMilliseconds(duration));
96 view->SetBoundsRect(gfx::Rect(end_position, view->size())); 104 view->SetBoundsRect(gfx::Rect(end_position, view->size()));
105 view->layer()->SetOpacity(opacity);
97 } 106 }
98 107
99 } // namespace 108 } // namespace
100 109
101 // Creates a throbbing animated view with two concentric circles. The radius of 110 // Creates a throbbing animated view with two concentric circles. The radius of
102 // the inner circle is fixed while that of the outer circle oscillates between a 111 // the inner circle is fixed while that of the outer circle oscillates between a
103 // min and max radius. The animation takes |animation_duration| milliseconds 112 // min and max radius. The animation takes |animation_duration| milliseconds
104 // to complete. The center of these circles are at the center of the view 113 // to complete. The center of these circles are at the center of the view
105 // element. 114 // element.
106 class CircularThrobberView : public views::View, public gfx::AnimationDelegate { 115 class CircularThrobberView : public views::View, public gfx::AnimationDelegate {
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 306
298 void HintBox::OnPaint(gfx::Canvas* canvas) { 307 void HintBox::OnPaint(gfx::Canvas* canvas) {
299 canvas->DrawRoundRect(GetLocalBounds(), border_radius_, paint_); 308 canvas->DrawRoundRect(GetLocalBounds(), border_radius_, paint_);
300 canvas->DrawStringRectWithFlags(label_text_, label_font_list_, label_color_, 309 canvas->DrawStringRectWithFlags(label_text_, label_font_list_, label_color_,
301 label_text_bounds_, gfx::Canvas::NO_ELLIPSIS); 310 label_text_bounds_, gfx::Canvas::NO_ELLIPSIS);
302 canvas->DrawStringRectWithFlags(sublabel_text_, sublabel_font_list_, 311 canvas->DrawStringRectWithFlags(sublabel_text_, sublabel_font_list_,
303 sublabel_color_, sublabel_text_bounds_, 312 sublabel_color_, sublabel_text_bounds_,
304 gfx::Canvas::NO_ELLIPSIS); 313 gfx::Canvas::NO_ELLIPSIS);
305 } 314 }
306 315
316 class CompletionMessageView : public views::View {
317 public:
318 CompletionMessageView(gfx::Rect bounds, base::string16 message);
oshima 2017/01/18 01:57:16 nit: const & for both
malaykeshav 2017/01/18 02:08:38 Done
319 ~CompletionMessageView() override;
320
321 // views::View overrides:
322 void OnPaint(gfx::Canvas* canvas) override;
323
324 private:
325 const base::string16 message_;
326 gfx::FontList font_list_;
327
328 int radius_;
329
330 gfx::Point center_;
oshima 2017/01/18 01:57:16 document these two.
malaykeshav 2017/01/18 02:08:38 No longer required.
331
332 gfx::Rect text_bounds_;
333
334 gfx::ImageSkia check_icon_;
335
336 SkPaint paint_;
337
338 DISALLOW_COPY_AND_ASSIGN(CompletionMessageView);
339 };
340
341 CompletionMessageView::CompletionMessageView(gfx::Rect bounds,
342 base::string16 message)
343 : message_(message) {
344 SetBoundsRect(bounds);
345
346 center_.SetPoint(height() / 2, height() / 2);
347 radius_ = height() / 2;
348
349 int x_offset = height() * 5.f / 4.f;
350 text_bounds_.SetRect(x_offset, 0, width() - x_offset, height());
351
352 font_list_ = ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
353 kCompleteMessageTextSize, gfx::Font::FontStyle::NORMAL,
354 gfx::Font::Weight::NORMAL);
355
356 // crbug/676513 moves this file to src/ash which will require an ash icon
357 // file.
358 check_icon_ = gfx::CreateVectorIcon(ash::kTouchCalibrationCompleteCheckIcon,
359 SK_ColorWHITE);
360
361 paint_.setColor(SK_ColorWHITE);
362 paint_.setStyle(SkPaint::kFill_Style);
363 paint_.setFlags(SkPaint::kAntiAlias_Flag);
364 }
365
366 CompletionMessageView::~CompletionMessageView() {}
367
368 void CompletionMessageView::OnPaint(gfx::Canvas* canvas) {
369 canvas->DrawImageInt(check_icon_, 0, 0);
370
371 // TODO(malaykeshav): Work with elizabethchiu@ to get better UX for RTL.
372 canvas->DrawStringRectWithFlags(
373 message_, font_list_, paint_.getColor(), text_bounds_,
374 gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::NO_SUBPIXEL_RENDERING);
375 }
376
307 TouchCalibratorView::TouchCalibratorView(const display::Display& target_display, 377 TouchCalibratorView::TouchCalibratorView(const display::Display& target_display,
308 bool is_primary_view) 378 bool is_primary_view)
309 : display_(target_display), 379 : display_(target_display),
310 is_primary_view_(is_primary_view), 380 is_primary_view_(is_primary_view),
311 exit_label_(nullptr), 381 exit_label_(nullptr),
312 tap_label_(nullptr), 382 tap_label_(nullptr),
313 throbber_circle_(nullptr), 383 throbber_circle_(nullptr),
314 hint_box_view_(nullptr), 384 hint_box_view_(nullptr),
315 touch_point_view_(nullptr) { 385 touch_point_view_(nullptr) {
316 aura::Window* root = ash::Shell::GetInstance() 386 aura::Window* root = ash::Shell::GetInstance()
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 touch_point_view_->y() + (tpv_width / 2.f) - (size.height() / 2.f)); 491 touch_point_view_->y() + (tpv_width / 2.f) - (size.height() / 2.f));
422 492
423 HintBox* hint_box = 493 HintBox* hint_box =
424 new HintBox(gfx::Rect(position, size), kHintRectBorderRadius); 494 new HintBox(gfx::Rect(position, size), kHintRectBorderRadius);
425 hint_box->SetVisible(false); 495 hint_box->SetVisible(false);
426 hint_box->SetLabel(hint_label_text, kHintLabelTextColor); 496 hint_box->SetLabel(hint_label_text, kHintLabelTextColor);
427 hint_box->SetSubLabel(hint_sublabel_text, kHintSublabelTextColor); 497 hint_box->SetSubLabel(hint_sublabel_text, kHintSublabelTextColor);
428 hint_box_view_ = hint_box; 498 hint_box_view_ = hint_box;
429 499
430 AddChildView(hint_box_view_); 500 AddChildView(hint_box_view_);
501
502 // Initialize the view that contains the calibration complete message which
503 // will be displayed at the end.
504 base::string16 finish_msg_text =
505 rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_FINISH_LABEL);
506
507 gfx::Rect msg_view_bounds(
508 (display_.bounds().width() - kCompleteMessageViewWidth) / 2,
509 display_.bounds().height() / 3, kCompleteMessageViewWidth,
510 kCompleteMessageViewHeight);
511 completion_message_view_ =
512 new CompletionMessageView(msg_view_bounds, finish_msg_text);
513 completion_message_view_->SetVisible(false);
514 completion_message_view_->SetPaintToLayer(true);
515 completion_message_view_->layer()->SetFillsBoundsOpaquely(false);
516 completion_message_view_->layer()->GetAnimator()->AddObserver(this);
517 completion_message_view_->set_background(
518 views::Background::CreateSolidBackground(SK_ColorTRANSPARENT));
519
520 AddChildView(completion_message_view_);
431 } 521 }
432 522
433 void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) { 523 void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) {
434 OnPaintBackground(canvas); 524 OnPaintBackground(canvas);
435 } 525 }
436 526
437 void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) { 527 void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) {
438 float opacity; 528 float opacity;
439 529
440 // If current state is a fade in or fade out state then update opacity 530 // If current state is a fade in or fade out state then update opacity
(...skipping 25 matching lines...) Expand all
466 void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) { 556 void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) {
467 switch (state_) { 557 switch (state_) {
468 case BACKGROUND_FADING_IN: 558 case BACKGROUND_FADING_IN:
469 exit_label_->SetVisible(true); 559 exit_label_->SetVisible(true);
470 state_ = is_primary_view_ ? DISPLAY_POINT_1 : CALIBRATION_COMPLETE; 560 state_ = is_primary_view_ ? DISPLAY_POINT_1 : CALIBRATION_COMPLETE;
471 if (is_primary_view_) { 561 if (is_primary_view_) {
472 touch_point_view_->SetVisible(true); 562 touch_point_view_->SetVisible(true);
473 hint_box_view_->SetVisible(true); 563 hint_box_view_->SetVisible(true);
474 } 564 }
475 break; 565 break;
566 case BACKGROUND_FADING_OUT:
567 exit_label_->SetVisible(false);
568 if (is_primary_view_)
569 completion_message_view_->SetVisible(false);
570 widget_->Hide();
571 break;
oshima 2017/01/18 01:57:16 You can do it in a separate CL, but you should use
malaykeshav 2017/01/18 02:08:38 Ack
476 default: 572 default:
477 break; 573 break;
478 } 574 }
479 } 575 }
480 576
481 void TouchCalibratorView::OnLayerAnimationStarted( 577 void TouchCalibratorView::OnLayerAnimationStarted(
482 ui::LayerAnimationSequence* sequence) {} 578 ui::LayerAnimationSequence* sequence) {}
483 579
484 void TouchCalibratorView::OnLayerAnimationEnded( 580 void TouchCalibratorView::OnLayerAnimationEnded(
485 ui::LayerAnimationSequence* sequence) { 581 ui::LayerAnimationSequence* sequence) {
486 switch (state_) { 582 switch (state_) {
487 case ANIMATING_1_TO_2: 583 case ANIMATING_1_TO_2:
488 state_ = DISPLAY_POINT_2; 584 state_ = DISPLAY_POINT_2;
489 tap_label_->SetVisible(true); 585 tap_label_->SetVisible(true);
490 break; 586 break;
491 case ANIMATING_2_TO_3: 587 case ANIMATING_2_TO_3:
492 state_ = DISPLAY_POINT_3; 588 state_ = DISPLAY_POINT_3;
493 break; 589 break;
494 case ANIMATING_3_TO_4: 590 case ANIMATING_3_TO_4:
495 state_ = DISPLAY_POINT_4; 591 state_ = DISPLAY_POINT_4;
496 break; 592 break;
593 case ANIMATING_FINAL_MESSAGE:
594 state_ = CALIBRATION_COMPLETE;
595 break;
497 default: 596 default:
498 break; 597 break;
499 } 598 }
500 } 599 }
501 600
502 void TouchCalibratorView::OnLayerAnimationAborted( 601 void TouchCalibratorView::OnLayerAnimationAborted(
503 ui::LayerAnimationSequence* sequence) { 602 ui::LayerAnimationSequence* sequence) {
504 OnLayerAnimationEnded(sequence); 603 OnLayerAnimationEnded(sequence);
505 } 604 }
506 605
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 state_ = ANIMATING_3_TO_4; 648 state_ = ANIMATING_3_TO_4;
550 649
551 // The touch point has to be animated from the bottom left corner of the 650 // The touch point has to be animated from the bottom left corner of the
552 // screen to the bottom right corner. 651 // screen to the bottom right corner.
553 AnimateLayerToPosition( 652 AnimateLayerToPosition(
554 touch_point_view_, kPointMoveDurationInMs, 653 touch_point_view_, kPointMoveDurationInMs,
555 gfx::Point(display_.bounds().width() - kTouchPointViewOffset - 654 gfx::Point(display_.bounds().width() - kTouchPointViewOffset -
556 touch_point_view_->width(), 655 touch_point_view_->width(),
557 touch_point_view_->y())); 656 touch_point_view_->y()));
558 return; 657 return;
658 case DISPLAY_POINT_4:
659 state_ = ANIMATING_FINAL_MESSAGE;
660 completion_message_view_->layer()->SetOpacity(0.f);
661 completion_message_view_->SetVisible(true);
662
663 touch_point_view_->SetVisible(false);
664
665 AnimateLayerToPosition(completion_message_view_,
666 kFinalMessageTransitionDurationMs,
667 gfx::Point(completion_message_view_->x(),
668 display_.bounds().height() / 2));
669 return;
670 case CALIBRATION_COMPLETE:
671 state_ = BACKGROUND_FADING_OUT;
672 if (is_primary_view_) {
673 // In case of primary view, we also need to fade out the calibration
674 // complete message view.
675 AnimateLayerToPosition(
676 completion_message_view_, kFadeDurationInMs,
677 gfx::Point(completion_message_view_->x(),
678 completion_message_view_->y() +
679 2 * completion_message_view_->height()),
680 0.f);
681 }
682
683 start_opacity_value_ = kBackgroundFinalOpacity;
684 end_opacity_value_ = 0.f;
685
686 paint_.setStyle(SkPaint::kFill_Style);
687 animator_->SetDuration(kFadeDurationInMs);
688 animator_->Start();
689 return;
559 default: 690 default:
560 return; 691 return;
561 } 692 }
562 } 693 }
563 694
564 bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) { 695 bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) {
565 DCHECK(location); 696 DCHECK(location);
566 if (!is_primary_view_) 697 if (!is_primary_view_)
567 return false; 698 return false;
568 699
569 if (state_ != DISPLAY_POINT_1 && state_ != DISPLAY_POINT_2 && 700 if (state_ != DISPLAY_POINT_1 && state_ != DISPLAY_POINT_2 &&
570 state_ != DISPLAY_POINT_3 && state_ != DISPLAY_POINT_4) { 701 state_ != DISPLAY_POINT_3 && state_ != DISPLAY_POINT_4) {
571 return false; 702 return false;
572 } 703 }
573 704
574 if (!touch_point_view_ || !throbber_circle_) 705 if (!touch_point_view_ || !throbber_circle_)
575 return false; 706 return false;
576 // TODO(malaykeshav): Can use views::ConvertPointToScreen() 707 // TODO(malaykeshav): Can use views::ConvertPointToScreen()
577 location->SetPoint(touch_point_view_->x() + touch_point_view_->width() / 2.f, 708 location->SetPoint(touch_point_view_->x() + touch_point_view_->width() / 2.f,
578 touch_point_view_->y() + touch_point_view_->width() / 2.f); 709 touch_point_view_->y() + touch_point_view_->width() / 2.f);
579 return true; 710 return true;
580 } 711 }
581 712
582 void TouchCalibratorView::SkipToFinalState() {} 713 void TouchCalibratorView::SkipToFinalState() {
714 state_ = CALIBRATION_COMPLETE;
715
716 exit_label_->SetVisible(false);
717
718 if (is_primary_view_) {
719 touch_point_view_->SetVisible(false);
720 hint_box_view_->SetVisible(false);
721 }
722
723 AdvanceToNextState();
724 }
583 725
584 void TouchCalibratorView::SkipCurrentAnimation() { 726 void TouchCalibratorView::SkipCurrentAnimation() {
585 if (animator_->is_animating()) 727 if (animator_->is_animating())
586 animator_->End(); 728 animator_->End();
587 if (touch_point_view_ && 729 if (touch_point_view_ &&
588 touch_point_view_->layer()->GetAnimator()->is_animating()) { 730 touch_point_view_->layer()->GetAnimator()->is_animating()) {
589 touch_point_view_->layer()->GetAnimator()->StopAnimating(); 731 touch_point_view_->layer()->GetAnimator()->StopAnimating();
590 } 732 }
591 } 733 }
592 734
593 } // namespace chromeos 735 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698