// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/display/touch_calibrator_view.h" #include #include "ash/public/cpp/shell_window_ids.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/shell.h" #include "base/memory/ptr_util.h" #include "ui/aura/window.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/label.h" #include "ui/views/widget/widget.h" namespace ash { namespace { constexpr char kWidgetName[] = "TouchCalibratorOverlay"; constexpr int kAnimationFrameRate = 100; constexpr auto kFadeDuration = base::Milliseconds(150); constexpr auto kPointMoveDuration = base::Milliseconds(400); constexpr auto kPointMoveDurationLong = base::Milliseconds(500); const SkColor kExitLabelColor = SkColorSetARGB(255, 138, 138, 138); constexpr int kExitLabelWidth = 300; constexpr int kExitLabelHeight = 20; const SkColor kTapHereLabelColor = SK_ColorWHITE; constexpr int kHintBoxWidth = 298; constexpr int kHintBoxHeight = 180; constexpr int kHintBoxLabelTextSize = 5; constexpr int kHintBoxSublabelTextSize = 3; constexpr int kThrobberCircleViewWidth = 64; constexpr float kThrobberCircleRadiusFactor = 3.f / 8.f; constexpr auto kFinalMessageTransitionDuration = base::Milliseconds(200); constexpr int kCompleteMessageViewWidth = 427; constexpr int kCompleteMessageViewHeight = kThrobberCircleViewWidth; constexpr int kCompleteMessageTextSize = 16; constexpr int kTouchPointViewOffset = 100; constexpr int kTapLabelHeight = 48; constexpr int kTapLabelWidth = 80; const SkColor kHintLabelTextColor = SK_ColorBLACK; const SkColor kHintSublabelTextColor = SkColorSetARGB(255, 161, 161, 161); const SkColor kInnerCircleColor = SK_ColorWHITE; const SkColor kOuterCircleColor = SkColorSetA(kInnerCircleColor, 255 * 0.2); constexpr auto kCircleAnimationDuration = base::Milliseconds(900); constexpr int kHintRectBorderRadius = 4; constexpr float kBackgroundFinalOpacity = 0.75f; constexpr int kTouchTargetWidth = 64; constexpr int kTouchTargetHeight = kTouchTargetWidth + kTouchTargetWidth / 2; constexpr float kTouchTargetVerticalOffsetFactor = 11.f / 24.f; const SkColor kTouchTargetInnerCircleColor = SkColorSetARGB(255, 66, 133, 244); const SkColor kTouchTargetOuterCircleColor = SkColorSetA(kTouchTargetInnerCircleColor, 255 * 0.2); const SkColor kHandIconColor = SkColorSetARGB(255, 201, 201, 201); constexpr float kHandIconHorizontalOffsetFactor = 7.f / 32.f; // Returns the initialization params for the widget that contains the touch // calibrator view. views::Widget::InitParams GetWidgetParams(aura::Window* root_window) { views::Widget::InitParams params; params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; params.name = kWidgetName; params.z_order = ui::ZOrderLevel::kFloatingWindow; params.accept_events = true; params.activatable = views::Widget::InitParams::Activatable::kNo; params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; params.parent = Shell::GetContainer(root_window, kShellWindowId_OverlayContainer); return params; } // Returns the size of bounding box required for |text| of given |font_list|. gfx::Size GetSizeForString(const std::u16string& text, const gfx::FontList& font_list) { int height = 0, width = 0; gfx::Canvas::SizeStringInt(text, font_list, &width, &height, 0, 0); return gfx::Size(width, height); } void AnimateLayerToPosition(views::View* view, base::TimeDelta duration, gfx::Point end_position, float opacity = 1.f) { ui::ScopedLayerAnimationSettings slide_settings(view->layer()->GetAnimator()); slide_settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); slide_settings.SetTransitionDuration(duration); view->SetBoundsRect(gfx::Rect(end_position, view->size())); view->layer()->SetOpacity(opacity); } } // namespace // Creates a throbbing animated view with two concentric circles. The radius of // the inner circle is fixed while that of the outer circle oscillates between a // min and max radius. The animation takes |animation_duration| milliseconds // to complete. The center of these circles are at the center of the view // element. class CircularThrobberView : public views::View, public views::AnimationDelegateViews { public: CircularThrobberView(int width, const SkColor& inner_circle_color, const SkColor& outer_circle_color, base::TimeDelta animation_duration); CircularThrobberView(const CircularThrobberView&) = delete; CircularThrobberView& operator=(const CircularThrobberView&) = delete; ~CircularThrobberView() override; // views::View: void OnPaint(gfx::Canvas* canvas) override; // views::AnimationDelegateViews: void AnimationProgressed(const gfx::Animation* animation) override; private: // Radius of the inner circle. const int inner_radius_; // Current radius of the outer circle. int outer_radius_; // Minimum radius for outer animated circle. const int smallest_radius_animated_circle_; // Maximum radius for outer animated circle. const int largest_radius_animated_circle_; cc::PaintFlags inner_circle_flags_; cc::PaintFlags outer_circle_flags_; std::unique_ptr animation_; // Center of the concentric circles. const gfx::Point center_; }; CircularThrobberView::CircularThrobberView(int width, const SkColor& inner_circle_color, const SkColor& outer_circle_color, base::TimeDelta animation_duration) : views::AnimationDelegateViews(this), inner_radius_(width / 4), outer_radius_(inner_radius_), smallest_radius_animated_circle_(width * kThrobberCircleRadiusFactor), largest_radius_animated_circle_(width / 2), center_(gfx::Point(width / 2, width / 2)) { SetSize(gfx::Size(width, width)); inner_circle_flags_.setColor(inner_circle_color); inner_circle_flags_.setAntiAlias(true); inner_circle_flags_.setStyle(cc::PaintFlags::kFill_Style); outer_circle_flags_.setColor(outer_circle_color); outer_circle_flags_.setAntiAlias(true); outer_circle_flags_.setStyle(cc::PaintFlags::kFill_Style); animation_ = std::make_unique(this); animation_->SetThrobDuration(animation_duration); animation_->StartThrobbing(-1); SchedulePaint(); } CircularThrobberView::~CircularThrobberView() = default; void CircularThrobberView::OnPaint(gfx::Canvas* canvas) { canvas->DrawCircle(center_, outer_radius_, outer_circle_flags_); canvas->DrawCircle(center_, inner_radius_, inner_circle_flags_); } void CircularThrobberView::AnimationProgressed( const gfx::Animation* animation) { if (animation != animation_.get()) return; outer_radius_ = animation->CurrentValueBetween( smallest_radius_animated_circle_, largest_radius_animated_circle_); SchedulePaint(); } class TouchTargetThrobberView : public CircularThrobberView { public: TouchTargetThrobberView(const gfx::Rect& bounds, const SkColor& inner_circle_color, const SkColor& outer_circle_color, const SkColor& hand_icon_color, base::TimeDelta animation_duration); TouchTargetThrobberView(const TouchTargetThrobberView&) = delete; TouchTargetThrobberView& operator=(const TouchTargetThrobberView&) = delete; ~TouchTargetThrobberView() override; // views::View: void OnPaint(gfx::Canvas* canvas) override; private: const int horizontal_offset_; const int icon_width_; gfx::ImageSkia hand_icon_; }; TouchTargetThrobberView::TouchTargetThrobberView( const gfx::Rect& bounds, const SkColor& inner_circle_color, const SkColor& outer_circle_color, const SkColor& hand_icon_color, base::TimeDelta animation_duration) : CircularThrobberView(bounds.width(), inner_circle_color, outer_circle_color, animation_duration), horizontal_offset_(bounds.width() * kHandIconHorizontalOffsetFactor), icon_width_(bounds.width() * 0.5f) { SetBoundsRect(bounds); hand_icon_ = gfx::CreateVectorIcon(kTouchCalibrationHandIcon, kHandIconColor); } TouchTargetThrobberView::~TouchTargetThrobberView() = default; void TouchTargetThrobberView::OnPaint(gfx::Canvas* canvas) { CircularThrobberView::OnPaint(canvas); canvas->DrawImageInt(hand_icon_, horizontal_offset_, icon_width_); } // Circular _________________________________ // Throbber | | // View | | // ___________ | | // | | | | // | | | | // | . | | Hint Box | // | | | | // |___________| | | // | | // | | // |_________________________________| // // This view is set next to the throbber circle view such that their centers // align. The hint box has a label text and a sublabel text to assist the // user by informing them about the next step in the calibration process. class HintBox : public views::View { public: HintBox(const gfx::Rect& bounds, int border_radius); HintBox(const HintBox&) = delete; HintBox& operator=(const HintBox&) = delete; ~HintBox() override; // views::View: void OnPaint(gfx::Canvas* canvas) override; void SetLabel(const std::u16string& text, const SkColor& color); void SetSubLabel(const std::u16string& text, const SkColor& color); private: void UpdateWidth(int updated_width); std::u16string label_text_; std::u16string sublabel_text_; SkColor label_color_; SkColor sublabel_color_; const int border_radius_; int base_border_; int arrow_width_; int horizontal_offset_; gfx::Rect rounded_rect_bounds_; gfx::FontList label_font_list_; gfx::FontList sublabel_font_list_; gfx::Rect label_text_bounds_; gfx::Rect sublabel_text_bounds_; cc::PaintFlags flags_; }; HintBox::HintBox(const gfx::Rect& bounds, int border_radius) : border_radius_(border_radius) { auto border = std::make_unique( base::i18n::IsRTL() ? views::BubbleBorder::RIGHT_CENTER : views::BubbleBorder::LEFT_CENTER, views::BubbleBorder::NO_SHADOW); border->SetColor(SK_ColorWHITE); SetBorder(std::move(border)); arrow_width_ = (GetInsets().right() - GetInsets().left()) * (base::i18n::IsRTL() ? 1 : -1); // Border on all sides are the same except on the side of the arrow, in which // case the width of the arrow is additional. base_border_ = base::i18n::IsRTL() ? GetInsets().left() : GetInsets().right(); SetBounds(bounds.x(), bounds.y() - base_border_, bounds.width() + 2 * base_border_ + arrow_width_, bounds.height() + 2 * base_border_); rounded_rect_bounds_ = GetContentsBounds(); flags_.setColor(SK_ColorWHITE); flags_.setStyle(cc::PaintFlags::kFill_Style); flags_.setAntiAlias(true); horizontal_offset_ = arrow_width_ + base_border_ + rounded_rect_bounds_.width() * 0.08f; int top_offset = horizontal_offset_; int line_gap = rounded_rect_bounds_.height() * 0.018f; int label_height = rounded_rect_bounds_.height() * 0.11f; label_text_bounds_.SetRect(horizontal_offset_, top_offset, 0, label_height); top_offset += label_text_bounds_.height() + line_gap; sublabel_text_bounds_.SetRect(horizontal_offset_, top_offset, 0, label_height); } HintBox::~HintBox() = default; void HintBox::UpdateWidth(int updated_width) { SetSize(gfx::Size(updated_width + 2 * base_border_ + arrow_width_, height())); rounded_rect_bounds_ = GetContentsBounds(); } void HintBox::SetLabel(const std::u16string& text, const SkColor& color) { label_text_ = text; label_color_ = color; label_font_list_ = ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( kHintBoxLabelTextSize); // Adjust size of label bounds based on text and font. gfx::Size size = GetSizeForString(label_text_, label_font_list_); label_text_bounds_.set_size( gfx::Size(size.width(), label_text_bounds_.height())); // Check if the width of hint box needs to be updated. int minimum_expected_width = size.width() + 2 * horizontal_offset_ - arrow_width_; if (minimum_expected_width > rounded_rect_bounds_.width()) UpdateWidth(minimum_expected_width); } void HintBox::SetSubLabel(const std::u16string& text, const SkColor& color) { sublabel_text_ = text; sublabel_color_ = color; sublabel_font_list_ = ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( kHintBoxSublabelTextSize); // Adjust size of sublabel label bounds based on text and font. gfx::Size size = GetSizeForString(sublabel_text_, sublabel_font_list_); sublabel_text_bounds_.set_size( gfx::Size(size.width(), sublabel_text_bounds_.height())); // Check if the width of hint box needs to be updated. int minimum_expected_width = size.width() + 2 * horizontal_offset_ - arrow_width_; if (minimum_expected_width > rounded_rect_bounds_.width()) UpdateWidth(minimum_expected_width); } void HintBox::OnPaint(gfx::Canvas* canvas) { views::View::OnPaint(canvas); canvas->DrawRoundRect(rounded_rect_bounds_, border_radius_, flags_); canvas->DrawStringRectWithFlags(label_text_, label_font_list_, label_color_, label_text_bounds_, gfx::Canvas::NO_ELLIPSIS); canvas->DrawStringRectWithFlags(sublabel_text_, sublabel_font_list_, sublabel_color_, sublabel_text_bounds_, gfx::Canvas::NO_ELLIPSIS); } class CompletionMessageView : public views::View { public: CompletionMessageView(const gfx::Rect& bounds, const std::u16string& message); CompletionMessageView(const CompletionMessageView&) = delete; CompletionMessageView& operator=(const CompletionMessageView&) = delete; ~CompletionMessageView() override; // views::View: void OnPaint(gfx::Canvas* canvas) override; private: const std::u16string message_; gfx::FontList font_list_; gfx::Rect text_bounds_; gfx::ImageSkia check_icon_; cc::PaintFlags flags_; }; CompletionMessageView::CompletionMessageView(const gfx::Rect& bounds, const std::u16string& message) : message_(message) { SetBoundsRect(bounds); int x_offset = height() * 5.f / 4.f; text_bounds_.SetRect(x_offset, 0, width() - x_offset, height()); font_list_ = ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( kCompleteMessageTextSize); // crbug/676513 moves this file to src/ash which will require an ash icon // file. check_icon_ = gfx::CreateVectorIcon(kTouchCalibrationCompleteCheckIcon, SK_ColorWHITE); flags_.setColor(SK_ColorWHITE); flags_.setStyle(cc::PaintFlags::kFill_Style); flags_.setAntiAlias(true); } CompletionMessageView::~CompletionMessageView() = default; void CompletionMessageView::OnPaint(gfx::Canvas* canvas) { canvas->DrawImageInt(check_icon_, 0, 0); // TODO(malaykeshav): Work with elizabethchiu@ to get better UX for RTL. canvas->DrawStringRectWithFlags( message_, font_list_, flags_.getColor(), text_bounds_, gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::NO_SUBPIXEL_RENDERING); } // static views::UniqueWidgetPtr TouchCalibratorView::Create( const display::Display& target_display, bool is_primary_view) { aura::Window* root = Shell::GetRootWindowForDisplayId(target_display.id()); views::UniqueWidgetPtr widget( std::make_unique(GetWidgetParams(root))); widget->SetContentsView(base::WrapUnique( new TouchCalibratorView(target_display, is_primary_view))); widget->SetBounds(target_display.bounds()); widget->Show(); return widget; } TouchCalibratorView::TouchCalibratorView(const display::Display& target_display, bool is_primary_view) : views::AnimationDelegateViews(this), display_(target_display), is_primary_view_(is_primary_view), animator_(std::make_unique(kFadeDuration, kAnimationFrameRate, this)) { InitViewContents(); AdvanceToNextState(); } TouchCalibratorView::~TouchCalibratorView() { state_ = UNKNOWN; animator_->End(); } void TouchCalibratorView::InitViewContents() { // Initialize the background rect. background_rect_ = gfx::RectF(0, 0, display_.bounds().width(), display_.bounds().height()); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); // Initialize exit label that informs the user how to exit the touch // calibration setup. exit_label_ = AddChildView(std::make_unique( rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_EXIT_LABEL), views::Label::CustomFont{rb.GetFontListWithDelta(8)})); exit_label_->SetBounds((display_.bounds().width() - kExitLabelWidth) / 2, display_.bounds().height() * 3.f / 4, kExitLabelWidth, kExitLabelHeight); exit_label_->SetAutoColorReadabilityEnabled(false); exit_label_->SetEnabledColor(kExitLabelColor); exit_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); exit_label_->SetSubpixelRenderingEnabled(false); exit_label_->SetVisible(false); // If this is not the screen that is being calibrated, then this is all we // need to display. if (!is_primary_view_) return; // Initialize the touch point view that contains the animated circle that the // user needs to tap. const int kTouchPointViewHeight = kThrobberCircleViewWidth + kTapLabelHeight; const int kThrobberCircleViewHorizontalOffset = (kTapLabelWidth - kThrobberCircleViewWidth) / 2; touch_point_view_ = AddChildView(std::make_unique()); touch_point_view_->SetBounds(kTouchPointViewOffset, kTouchPointViewOffset, kTapLabelWidth, kTouchPointViewHeight); touch_point_view_->SetVisible(false); touch_point_view_->SetPaintToLayer(); touch_point_view_->layer()->SetFillsBoundsOpaquely(false); touch_point_view_->layer()->GetAnimator()->AddObserver(this); touch_point_view_->SetBackground( views::CreateSolidBackground(SK_ColorTRANSPARENT)); throbber_circle_ = touch_point_view_->AddChildView(std::make_unique( kThrobberCircleViewWidth, kInnerCircleColor, kOuterCircleColor, kCircleAnimationDuration)); throbber_circle_->SetPosition( gfx::Point(kThrobberCircleViewHorizontalOffset, 0)); // Initialize the tap label. tap_label_ = touch_point_view_->AddChildView(std::make_unique( rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_TAP_HERE_LABEL), views::Label::CustomFont{rb.GetFontListWithDelta(6)})); tap_label_->SetBounds(0, kThrobberCircleViewWidth, kTapLabelWidth, kTapLabelHeight); tap_label_->SetEnabledColor(kTapHereLabelColor); tap_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); tap_label_->SetAutoColorReadabilityEnabled(false); tap_label_->SetSubpixelRenderingEnabled(false); tap_label_->SetVisible(false); // Initialize the Hint Box view. std::u16string hint_label_text = rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_LABEL_TEXT); std::u16string hint_sublabel_text = rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_SUBLABEL_TEXT); int tpv_width = touch_point_view_->width(); gfx::Size size(kHintBoxWidth, kHintBoxHeight); gfx::Point position(touch_point_view_->x() + tpv_width * 1.2f, touch_point_view_->y() + (kThrobberCircleViewWidth / 2.f) - (size.height() / 2.f)); hint_box_view_ = AddChildView(std::make_unique( gfx::Rect(position, size), kHintRectBorderRadius)); hint_box_view_->SetVisible(false); hint_box_view_->SetLabel(hint_label_text, kHintLabelTextColor); hint_box_view_->SetSubLabel(hint_sublabel_text, kHintSublabelTextColor); // Initialize the animated hint box throbber view. auto* target_view = hint_box_view_->AddChildView(std::make_unique( gfx::Rect((hint_box_view_->width() - kTouchTargetWidth) / 2, hint_box_view_->height() * kTouchTargetVerticalOffsetFactor, kTouchTargetWidth, kTouchTargetHeight), kTouchTargetInnerCircleColor, kTouchTargetOuterCircleColor, kHandIconColor, kCircleAnimationDuration)); target_view->SetVisible(true); // Initialize the view that contains the calibration complete message which // will be displayed at the end. std::u16string finish_msg_text = rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_FINISH_LABEL); gfx::Rect msg_view_bounds( (display_.bounds().width() - kCompleteMessageViewWidth) / 2, display_.bounds().height() / 3, kCompleteMessageViewWidth, kCompleteMessageViewHeight); completion_message_view_ = AddChildView(std::make_unique(msg_view_bounds, finish_msg_text)); completion_message_view_->SetVisible(false); completion_message_view_->SetPaintToLayer(); completion_message_view_->layer()->SetFillsBoundsOpaquely(false); completion_message_view_->layer()->GetAnimator()->AddObserver(this); completion_message_view_->SetBackground( views::CreateSolidBackground(SK_ColorTRANSPARENT)); } void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) { OnPaintBackground(canvas); } void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) { float opacity; // If current state is a fade in or fade out state then update opacity // based on how far the animation has progressed. if (animator_ && (state_ == TouchCalibratorView::BACKGROUND_FADING_OUT || state_ == TouchCalibratorView::BACKGROUND_FADING_IN)) { opacity = static_cast(animator_->CurrentValueBetween( start_opacity_value_, end_opacity_value_)); } else { opacity = state_ == BACKGROUND_FADING_OUT ? 0.0f : kBackgroundFinalOpacity; } flags_.setColor(SkColorSetA(SK_ColorBLACK, std::numeric_limits::max() * opacity)); canvas->DrawRect(background_rect_, flags_); } void TouchCalibratorView::AnimationProgressed(const gfx::Animation* animation) { if (!is_primary_view_) { SchedulePaint(); return; } } void TouchCalibratorView::AnimationCanceled(const gfx::Animation* animation) { AnimationEnded(animation); } void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) { switch (state_) { case BACKGROUND_FADING_IN: exit_label_->SetVisible(true); state_ = is_primary_view_ ? DISPLAY_POINT_1 : CALIBRATION_COMPLETE; if (is_primary_view_) { touch_point_view_->SetVisible(true); hint_box_view_->SetVisible(true); } break; case BACKGROUND_FADING_OUT: exit_label_->SetVisible(false); if (is_primary_view_) completion_message_view_->SetVisible(false); GetWidget()->Hide(); break; default: break; } } void TouchCalibratorView::OnLayerAnimationStarted( ui::LayerAnimationSequence* sequence) {} void TouchCalibratorView::OnLayerAnimationEnded( ui::LayerAnimationSequence* sequence) { switch (state_) { case ANIMATING_1_TO_2: state_ = DISPLAY_POINT_2; tap_label_->SetVisible(true); break; case ANIMATING_2_TO_3: state_ = DISPLAY_POINT_3; break; case ANIMATING_3_TO_4: state_ = DISPLAY_POINT_4; break; case ANIMATING_FINAL_MESSAGE: state_ = CALIBRATION_COMPLETE; break; default: break; } } void TouchCalibratorView::OnLayerAnimationAborted( ui::LayerAnimationSequence* sequence) { OnLayerAnimationEnded(sequence); } void TouchCalibratorView::OnLayerAnimationScheduled( ui::LayerAnimationSequence* sequence) {} void TouchCalibratorView::AdvanceToNextState() { // Stop any previous animations and skip them to the end. SkipCurrentAnimation(); switch (state_) { case UNKNOWN: case BACKGROUND_FADING_IN: state_ = BACKGROUND_FADING_IN; start_opacity_value_ = 0.0f; end_opacity_value_ = kBackgroundFinalOpacity; flags_.setStyle(cc::PaintFlags::kFill_Style); animator_->SetDuration(kFadeDuration); animator_->Start(); return; case DISPLAY_POINT_1: state_ = ANIMATING_1_TO_2; // The touch point has to be animated from the top left corner of the // screen to the top right corner. AnimateLayerToPosition( touch_point_view_, kPointMoveDuration, gfx::Point(display_.bounds().width() - kTouchPointViewOffset - touch_point_view_->width(), touch_point_view_->y())); hint_box_view_->SetVisible(false); return; case DISPLAY_POINT_2: state_ = ANIMATING_2_TO_3; // The touch point has to be animated from the top right corner of the // screen to the bottom left corner. AnimateLayerToPosition( touch_point_view_, kPointMoveDurationLong, gfx::Point(kTouchPointViewOffset, display_.bounds().height() - kTouchPointViewOffset - touch_point_view_->height())); return; case DISPLAY_POINT_3: state_ = ANIMATING_3_TO_4; // The touch point has to be animated from the bottom left corner of the // screen to the bottom right corner. AnimateLayerToPosition( touch_point_view_, kPointMoveDuration, gfx::Point(display_.bounds().width() - kTouchPointViewOffset - touch_point_view_->width(), touch_point_view_->y())); return; case DISPLAY_POINT_4: state_ = ANIMATING_FINAL_MESSAGE; completion_message_view_->layer()->SetOpacity(0.0f); completion_message_view_->SetVisible(true); touch_point_view_->SetVisible(false); AnimateLayerToPosition(completion_message_view_, kFinalMessageTransitionDuration, gfx::Point(completion_message_view_->x(), display_.bounds().height() / 2)); return; case CALIBRATION_COMPLETE: state_ = BACKGROUND_FADING_OUT; if (is_primary_view_) { // In case of primary view, we also need to fade out the calibration // complete message view. AnimateLayerToPosition( completion_message_view_, kFadeDuration, gfx::Point(completion_message_view_->x(), completion_message_view_->y() + 2 * completion_message_view_->height()), 0.0f); } start_opacity_value_ = kBackgroundFinalOpacity; end_opacity_value_ = 0.0f; flags_.setStyle(cc::PaintFlags::kFill_Style); animator_->SetDuration(kFadeDuration); animator_->Start(); return; default: return; } } bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) { DCHECK(location); if (!is_primary_view_) return false; if (state_ != DISPLAY_POINT_1 && state_ != DISPLAY_POINT_2 && state_ != DISPLAY_POINT_3 && state_ != DISPLAY_POINT_4) { return false; } if (!touch_point_view_ || !throbber_circle_) return false; // TODO(malaykeshav): Can use views::ConvertPointToScreen() location->SetPoint(touch_point_view_->x() + touch_point_view_->width() / 2.f, touch_point_view_->y() + touch_point_view_->width() / 2.f); return true; } void TouchCalibratorView::SkipToFinalState() { state_ = CALIBRATION_COMPLETE; exit_label_->SetVisible(false); if (is_primary_view_) { touch_point_view_->SetVisible(false); hint_box_view_->SetVisible(false); } AdvanceToNextState(); } void TouchCalibratorView::SkipCurrentAnimation() { if (animator_->is_animating()) animator_->End(); if (touch_point_view_ && touch_point_view_->layer()->GetAnimator()->is_animating()) { touch_point_view_->layer()->GetAnimator()->StopAnimating(); } } } // namespace ash