blob: ec9b5b6dba6889e322ca230345901b6a31b2e216 [file] [log] [blame]
// 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 <memory>
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ui/aura/window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/linear_animation.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::TimeDelta::FromMilliseconds(150);
constexpr auto kPointMoveDuration = base::TimeDelta::FromMilliseconds(400);
constexpr auto kPointMoveDurationLong = base::TimeDelta::FromMilliseconds(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::TimeDelta::FromMilliseconds(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 int kCircleAnimationDurationMs = 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.keep_on_top = true;
params.accept_events = true;
params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
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 base::string16& 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 gfx::AnimationDelegate {
public:
CircularThrobberView(int width,
const SkColor& inner_circle_color,
const SkColor& outer_circle_color,
int animation_duration);
~CircularThrobberView() override;
// views::View overrides:
void OnPaint(gfx::Canvas* canvas) override;
// gfx::AnimationDelegate overrides:
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<gfx::ThrobAnimation> animation_;
// Center of the concentric circles.
const gfx::Point center_;
DISALLOW_COPY_AND_ASSIGN(CircularThrobberView);
};
CircularThrobberView::CircularThrobberView(int width,
const SkColor& inner_circle_color,
const SkColor& outer_circle_color,
int animation_duration)
: 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_.reset(new gfx::ThrobAnimation(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,
int animation_duration);
~TouchTargetThrobberView() override;
// views::View overrides:
void OnPaint(gfx::Canvas* canvas) override;
private:
const int horizontal_offset_;
const int icon_width_;
gfx::ImageSkia hand_icon_;
DISALLOW_COPY_AND_ASSIGN(TouchTargetThrobberView);
};
TouchTargetThrobberView::TouchTargetThrobberView(
const gfx::Rect& bounds,
const SkColor& inner_circle_color,
const SkColor& outer_circle_color,
const SkColor& hand_icon_color,
int 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() override;
// views::View overrides:
void OnPaint(gfx::Canvas* canvas) override;
void SetLabel(const base::string16& text, const SkColor& color);
void SetSubLabel(const base::string16& text, const SkColor& color);
private:
void UpdateWidth(int updated_width);
base::string16 label_text_;
base::string16 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_;
DISALLOW_COPY_AND_ASSIGN(HintBox);
};
HintBox::HintBox(const gfx::Rect& bounds, int border_radius)
: border_radius_(border_radius) {
SetBorder(std::make_unique<views::BubbleBorder>(
base::i18n::IsRTL() ? views::BubbleBorder::RIGHT_CENTER
: views::BubbleBorder::LEFT_CENTER,
views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER, SK_ColorWHITE));
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_ = GetLocalBounds();
rounded_rect_bounds_.Inset(GetInsets());
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_ = GetLocalBounds();
rounded_rect_bounds_.Inset(GetInsets());
}
void HintBox::SetLabel(const base::string16& text, const SkColor& color) {
label_text_ = text;
label_color_ = color;
label_font_list_ =
ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
kHintBoxLabelTextSize, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL);
// 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 base::string16& text, const SkColor& color) {
sublabel_text_ = text;
sublabel_color_ = color;
sublabel_font_list_ =
ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
kHintBoxSublabelTextSize, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL);
// 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 base::string16& message);
~CompletionMessageView() override;
// views::View overrides:
void OnPaint(gfx::Canvas* canvas) override;
private:
const base::string16 message_;
gfx::FontList font_list_;
gfx::Rect text_bounds_;
gfx::ImageSkia check_icon_;
cc::PaintFlags flags_;
DISALLOW_COPY_AND_ASSIGN(CompletionMessageView);
};
CompletionMessageView::CompletionMessageView(const gfx::Rect& bounds,
const base::string16& 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, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL);
// 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);
}
TouchCalibratorView::TouchCalibratorView(const display::Display& target_display,
bool is_primary_view)
: display_(target_display),
is_primary_view_(is_primary_view),
exit_label_(nullptr),
tap_label_(nullptr),
throbber_circle_(nullptr),
hint_box_view_(nullptr),
touch_point_view_(nullptr) {
aura::Window* root = Shell::GetRootWindowForDisplayId(display_.id());
widget_.reset(new views::Widget);
widget_->Init(GetWidgetParams(root));
widget_->SetContentsView(this);
widget_->SetBounds(display_.bounds());
widget_->Show();
set_owned_by_client();
animator_.reset(
new gfx::LinearAnimation(kFadeDuration, kAnimationFrameRate, this));
InitViewContents();
AdvanceToNextState();
}
TouchCalibratorView::~TouchCalibratorView() {
state_ = UNKNOWN;
widget_->Hide();
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_ = new views::Label(
rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_EXIT_LABEL),
{rb.GetFontListWithDelta(8, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL)});
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);
AddChildView(exit_label_);
// 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;
throbber_circle_ =
new CircularThrobberView(kThrobberCircleViewWidth, kInnerCircleColor,
kOuterCircleColor, kCircleAnimationDurationMs);
throbber_circle_->SetPosition(
gfx::Point(kThrobberCircleViewHorizontalOffset, 0));
// Initialize the tap label.
tap_label_ = new views::Label(
rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_TAP_HERE_LABEL),
{rb.GetFontListWithDelta(6, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL)});
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);
touch_point_view_ = new views::View;
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));
touch_point_view_->AddChildView(throbber_circle_);
touch_point_view_->AddChildView(tap_label_);
AddChildView(touch_point_view_);
// Initialize the Hint Box view.
base::string16 hint_label_text =
rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_LABEL_TEXT);
base::string16 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));
HintBox* hint_box =
new HintBox(gfx::Rect(position, size), kHintRectBorderRadius);
hint_box->SetVisible(false);
hint_box->SetLabel(hint_label_text, kHintLabelTextColor);
hint_box->SetSubLabel(hint_sublabel_text, kHintSublabelTextColor);
hint_box_view_ = hint_box;
AddChildView(hint_box_view_);
// Initialize the animated hint box throbber view.
TouchTargetThrobberView* target_view = new TouchTargetThrobberView(
gfx::Rect((hint_box->width() - kTouchTargetWidth) / 2,
hint_box->height() * kTouchTargetVerticalOffsetFactor,
kTouchTargetWidth, kTouchTargetHeight),
kTouchTargetInnerCircleColor, kTouchTargetOuterCircleColor,
kHandIconColor, kCircleAnimationDurationMs);
target_view->SetVisible(true);
hint_box_view_->AddChildView(target_view);
// Initialize the view that contains the calibration complete message which
// will be displayed at the end.
base::string16 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_ =
new CompletionMessageView(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));
AddChildView(completion_message_view_);
}
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<float>(animator_->CurrentValueBetween(
start_opacity_value_, end_opacity_value_));
} else {
opacity = state_ == BACKGROUND_FADING_OUT ? 0.f : kBackgroundFinalOpacity;
}
flags_.setColor(SkColorSetA(SK_ColorBLACK,
std::numeric_limits<uint8_t>::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);
widget_->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.f;
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.f);
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.f);
}
start_opacity_value_ = kBackgroundFinalOpacity;
end_opacity_value_ = 0.f;
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