// Copyright 2019 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/shelf/swipe_home_to_overview_controller.h" #include "ash/app_list/app_list_controller_impl.h" #include "ash/constants/ash_features.h" #include "ash/controls/contextual_tooltip.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/shelf_config.h" #include "ash/session/session_controller_impl.h" #include "ash/shelf/drag_window_from_shelf_controller.h" #include "ash/shelf/shelf_metrics.h" #include "ash/shell.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_session.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/cxx17_backports.h" #include "base/metrics/histogram_macros.h" #include "base/time/default_tick_clock.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/compositor/layer_animation_element.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/animation/tween.h" namespace ash { namespace { // The target/min home launcher view scale. constexpr float kTargetHomeScale = 0.92f; // The home UI will be scaled down towards center of the screen as drag location // moves upwards. The target threshold for scaling is extended above the actual // threshold for transition so the UI keeps changing even when the gesture goes // over the threshold. This is the target home screen scaling threshold in terms // of ratio of the display height. constexpr float kHomeScalingThresholdDisplayHeightRatio = 0.5f; // The amount of time the drag has to remain bellow velocity threshold before // the transition to the overview starts. constexpr base::TimeDelta kOverviewTransitionDelay = base::Milliseconds(150); // The duration of transition from the home screen current scaled state to the // initial (unscaled) state when the gesture is canceled. constexpr base::TimeDelta kGestureCancelationDuration = base::Milliseconds(350); // The duration of transition from the home screen current scaled state to the // initial (unscaled) state when the gesture is canceled due to a back gesture. constexpr base::TimeDelta kGestureCancelationForBackDuration = base::Milliseconds(250); void UpdateHomeAnimationForGestureCancel( bool going_back, ui::ScopedLayerAnimationSettings* settings) { settings->SetTransitionDuration(going_back ? kGestureCancelationForBackDuration : kGestureCancelationDuration); settings->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN); settings->SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); } } // namespace SwipeHomeToOverviewController::SwipeHomeToOverviewController(int64_t display_id) : SwipeHomeToOverviewController(display_id, base::DefaultTickClock::GetInstance()) {} SwipeHomeToOverviewController::SwipeHomeToOverviewController( int64_t display_id, const base::TickClock* tick_clock) : display_id_(display_id), overview_transition_timer_(tick_clock) {} SwipeHomeToOverviewController::~SwipeHomeToOverviewController() { CancelDrag(); } void SwipeHomeToOverviewController::Drag(const gfx::PointF& location_in_screen, float scroll_x, float scroll_y) { if (state_ == State::kFinished) return; display::Display display; if (!display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id_, &display)) { CancelDrag(); return; } const int shelf_top = display.bounds().bottom() - ShelfConfig::Get()->shelf_size(); if (state_ == State::kInitial) { // Do not start drag until the drag goes above the shelf. if (location_in_screen.y() > shelf_top) return; overview_transition_threshold_y_ = shelf_top - kVerticalThresholdForOverviewTransition; scaling_threshold_y_ = display.bounds().y() + display.bounds().height() * kHomeScalingThresholdDisplayHeightRatio; state_ = State::kTrackingDrag; home_screen_blur_disabler_ = Shell::Get()->app_list_controller()->DisableHomeScreenBackgroundBlur(); } else { if (location_in_screen.y() <= overview_transition_threshold_y_ && std::abs(scroll_x) + std::abs(scroll_y) <= kMovementVelocityThreshold) { ScheduleFinalizeDragAndShowOverview(); } else { overview_transition_timer_.Stop(); } } // Update the home screen scale to match progress during the drag. // Use extended threshold as the projected final transition position - UI // changing even after the user gets over the threshold should make the user // more likely to keep dragging up when they get really close to the threshold // for transition to overview (and reduce false negatives for detecting // transition to overview). const float distance = location_in_screen.y() - scaling_threshold_y_; const float target_distance = overview_transition_threshold_y_ - scaling_threshold_y_ + kVerticalThresholdForOverviewTransition; const float progress = gfx::Tween::CalculateValue( gfx::Tween::FAST_OUT_SLOW_IN, base::clamp(1.f - distance / target_distance, 0.0f, 1.0f)); float scale = gfx::Tween::FloatValueBetween(progress, 1.0f, kTargetHomeScale); Shell::Get()->app_list_controller()->UpdateScaleAndOpacityForHomeLauncher( scale, 1.0f /*opacity*/, absl::nullopt /*animation_info*/, base::NullCallback()); } void SwipeHomeToOverviewController::EndDrag( const gfx::PointF& location_in_screen, absl::optional velocity_y) { if (state_ != State::kTrackingDrag) { state_ = State::kFinished; return; } // Upward swipe should return to the home screen's initial state. const bool go_back = velocity_y && *velocity_y < -DragWindowFromShelfController::kVelocityToHomeScreenThreshold; // Overview is triggered by |overview_transition_timer_|. If EndDrag() // is called before the timer fires, the result of the gesture should be // staying on the home screen. FinalizeDragAndStayOnHomeScreen(go_back); } void SwipeHomeToOverviewController::CancelDrag() { if (state_ != State::kTrackingDrag) { state_ = State::kFinished; return; } FinalizeDragAndStayOnHomeScreen(/*go_back=*/false); } void SwipeHomeToOverviewController::ScheduleFinalizeDragAndShowOverview() { if (overview_transition_timer_.IsRunning()) return; overview_transition_timer_.Start( FROM_HERE, kOverviewTransitionDelay, base::BindOnce( &SwipeHomeToOverviewController::FinalizeDragAndShowOverview, base::Unretained(this))); } void SwipeHomeToOverviewController::FinalizeDragAndShowOverview() { state_ = State::kFinished; overview_transition_threshold_y_ = 0; if (features::AreContextualNudgesEnabled()) { contextual_tooltip::HandleGesturePerformed( Shell::Get()->session_controller()->GetActivePrefService(), contextual_tooltip::TooltipType::kHomeToOverview); } UMA_HISTOGRAM_ENUMERATION(kEnterOverviewHistogramName, EnterOverviewFromHomeLauncher::kOverview); // NOTE: No need to update the home launcher opacity and scale here - the // AppListControllerImpl will update the home launcher state when it detects // that the overview is starting. Shell::Get()->overview_controller()->StartOverview( OverviewStartAction::kExitHomeLauncher); // No need to keep blur disabled for the drag - note that blur might remain // disabled at this point due to the started overview transition (which // triggers home screen scale animation). home_screen_blur_disabler_.reset(); } void SwipeHomeToOverviewController::FinalizeDragAndStayOnHomeScreen( bool go_back) { overview_transition_timer_.Stop(); overview_transition_threshold_y_ = 0; state_ = State::kFinished; if (go_back) { Shell::Get()->app_list_controller()->Back(); UMA_HISTOGRAM_ENUMERATION(kEnterOverviewHistogramName, EnterOverviewFromHomeLauncher::kBack); } else { UMA_HISTOGRAM_ENUMERATION(kEnterOverviewHistogramName, EnterOverviewFromHomeLauncher::kCanceled); } // Make sure the home launcher scale and opacity return to the initial state. // Note that this is needed even if the gesture ended up in a fling, as early // gesture handling might have updated the launcher scale. Shell::Get()->app_list_controller()->UpdateScaleAndOpacityForHomeLauncher( 1.0f /*scale*/, 1.0f /*opacity*/, absl::nullopt /*animation_info*/, base::BindRepeating(&UpdateHomeAnimationForGestureCancel, go_back)); // No need to keep blur disabled for the drag - note that blur might remain // disabled at this point due to the started home screen scale animation. home_screen_blur_disabler_.reset(); } } // namespace ash