chromium/ash/app_list/views/app_list_view.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_APP_LIST_VIEWS_APP_LIST_VIEW_H_
#define ASH_APP_LIST_VIEWS_APP_LIST_VIEW_H_

#include <memory>
#include <optional>

#include "ash/app_list/app_list_metrics.h"
#include "ash/app_list/app_list_view_delegate.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/metrics_util.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "ui/aura/window_observer.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/events/event.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace aura {
class Window;
}

namespace display {
class Display;
}

namespace ui {
class ImplicitAnimationObserver;
}  // namespace ui

namespace ash {
class AppListA11yAnnouncer;
class AppsContainerView;
class ApplicationDragAndDropHost;
class AppListMainView;
class AppsGridView;
class PagedAppsGridView;
class PaginationModel;
class SearchBoxView;

FORWARD_DECLARE_TEST(AppListControllerImplTest,
                     CheckAppListViewBoundsWhenDismissVKeyboard);

// AppListView is the top-level view and controller of app list UI. It creates
// and hosts a AppsGridView and passes AppListModel to it for display.
// TODO(newcomer|weidongg): Organize the cc file to match the order of
// definitions in this header.
class ASH_EXPORT AppListView : public views::WidgetDelegateView,
                               public aura::WindowObserver {
  METADATA_HEADER(AppListView, views::WidgetDelegateView)
 public:
  class TestApi {
   public:
    explicit TestApi(AppListView* view);

    TestApi(const TestApi&) = delete;
    TestApi& operator=(const TestApi&) = delete;

    ~TestApi();

    PagedAppsGridView* GetRootAppsGridView();

   private:
    const raw_ptr<AppListView> view_;
  };

  class ASH_EXPORT ScopedAccessibilityAnnouncementLock {
   public:
    explicit ScopedAccessibilityAnnouncementLock(AppListView* view)
        : view_(view) {
      ++view_->accessibility_event_disablers_;
    }

    ~ScopedAccessibilityAnnouncementLock() {
      --view_->accessibility_event_disablers_;
    }

   private:
    const raw_ptr<AppListView> view_;
  };

  // Used to prevent the app list contents from being reset when the app list
  // shows. Only one instance can exist at a time. This class is useful when:
  // (1) the app list close animation is reversed, and
  // (2) the contents before the close animation starts should be kept.
  class ScopedContentsResetDisabler {
   public:
    explicit ScopedContentsResetDisabler(AppListView* view);
    ScopedContentsResetDisabler(const ScopedContentsResetDisabler&) = delete;
    ScopedContentsResetDisabler& operator=(const ScopedContentsResetDisabler&) =
        delete;
    ~ScopedContentsResetDisabler();

   private:
    const raw_ptr<AppListView> view_;
  };

  // Does not take ownership of |delegate|.
  explicit AppListView(AppListViewDelegate* delegate);

  AppListView(const AppListView&) = delete;
  AppListView& operator=(const AppListView&) = delete;

  ~AppListView() override;

  // Used for testing, allows the page reset timer to be fired immediately
  // after starting.
  static void SetSkipPageResetTimerForTesting(bool enabled);

  // Initializes the view, only done once per session.
  void InitView(gfx::NativeView parent);

  // Initializes the contents of the view.
  void InitContents();

  // Initializes this view's widget.
  void InitWidget(gfx::NativeView parent);

  // Sets the state of all child views to be re-shown, then shows the view.
  // |preferred_state| - The initial app list view state.
  void Show(AppListViewState preferred_state);

  // If |drag_and_drop_host| is not nullptr it will be called upon drag and drop
  // operations outside the application list. This has to be called after
  // Initialize was called since the app list object needs to exist so that
  // it can set the host.
  void SetDragAndDropHostOfCurrentAppList(
      ApplicationDragAndDropHost* drag_and_drop_host);

  // Resets the child views before showing the AppListView.
  void ResetForShow();

  // Closes opened folder or search result page if they are opened.
  void CloseOpenedPage();

  // If a folder is open, close it. Returns whether an opened folder was closed.
  bool HandleCloseOpenFolder();

  // If a search box is open, close it. Returns whether an open search box was
  // closed.
  bool HandleCloseOpenSearchBox();

  // Performs the 'back' action for the active page.
  bool Back();

  // views::View:
  void OnPaint(gfx::Canvas* canvas) override;
  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
  void Layout(PassKey) override;

  // ui::EventHandler:
  void OnKeyEvent(ui::KeyEvent* event) override;
  void OnScrollEvent(ui::ScrollEvent* event) override;
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnGestureEvent(ui::GestureEvent* event) override;

  // Handles scroll events from various sources.
  bool HandleScroll(const gfx::Point& location,
                    const gfx::Vector2d& offset,
                    ui::EventType type);

  // Changes the app list state.
  void SetState(AppListViewState new_state);

  // Changes the app list state depending on the current |app_list_state_| and
  // whether the search box is empty.
  void SetStateFromSearchBoxView(bool search_box_is_empty,
                                 bool triggered_by_contents_change);

  // Offsets the y position of the app list (above the screen)
  void OffsetYPositionOfAppList(int offset);

  // The search box cannot actively listen to all key events. To control and
  // input into the search box when it does not have focus, we need to redirect
  // necessary key events to the search box.
  void RedirectKeyEventToSearchBox(ui::KeyEvent* event);

  // Called when on-screen keyboard's visibility is changed.
  void OnScreenKeyboardShown(bool shown);

  // Called when parent window's bounds is changed.
  void OnParentWindowBoundsChanged();

  // If the on-screen keyboard is shown, hide it. Return whether keyboard was
  // hidden
  bool CloseKeyboardIfVisible();

  // Home launcher can become the focused window without being reset when all
  // open windows are closed in tablet mode. Ensures that correct initial view
  // is focused in this case.
  void OnHomeLauncherGainingFocusWithoutAnimation();

  // Ensures that apps grid pagination model has selected the initial page.
  void SelectInitialAppsPage();

  // Gets the PaginationModel owned by this view's apps grid.
  PaginationModel* GetAppsPaginationModel();

  // Returns the height of app list in fullscreen state.
  int GetFullscreenStateHeight() const;

  // Returns a animation metrics reporting callback  for state transition.
  metrics_util::SmoothnessCallback GetStateTransitionMetricsReportCallback();

  // Resets the animation metrics reporter for state transition.
  void ResetTransitionMetricsReporter();

  // WindowObserver overrides:
  void OnWindowDestroying(aura::Window* window) override;

  void OnTabletModeAnimationTransitionNotified(
      TabletModeAnimationTransition animation_transition);

  AppListViewState app_list_state() const { return app_list_state_; }

  SearchBoxView* search_box_view() const { return search_box_view_; }

  AppListMainView* app_list_main_view() const { return app_list_main_view_; }

  AppListA11yAnnouncer* a11y_announcer() { return a11y_announcer_.get(); }

  bool is_fullscreen() const {
    return app_list_state_ == AppListViewState::kFullscreenAllApps ||
           app_list_state_ == AppListViewState::kFullscreenSearch;
  }

  void set_onscreen_keyboard_shown(bool onscreen_keyboard_shown) {
    onscreen_keyboard_shown_ = onscreen_keyboard_shown;
  }

  // Returns true if the Embedded Assistant UI is currently being shown.
  bool IsShowingEmbeddedAssistantUI() const;

  // Returns true if a folder is being renamed.
  bool IsFolderBeingRenamed();

  // Starts or stops a timer which will reset the app list to the initial apps
  // page. Called when the app list's visibility changes.
  void UpdatePageResetTimer(bool app_list_visibility);

  // Updates the title of the window that contains the launcher.
  void UpdateWindowTitle();

  // Called when app list visibility changed.
  void OnAppListVisibilityWillChange(bool visible);
  void OnAppListVisibilityChanged(bool shown);

 private:
  FRIEND_TEST_ALL_PREFIXES(AppListControllerImplTest,
                           CheckAppListViewBoundsWhenDismissVKeyboard);

  class StateAnimationMetricsReporter;

  // Returns insets that should be added to app list content to avoid overlap
  // with the shelf.
  gfx::Insets GetMainViewInsetsForShelf() const;

  // Updates the widget to be shown.
  void UpdateWidget();

  // Closes the AppListView when a click or tap event propogates to the
  // AppListView.
  void HandleClickOrTap(ui::LocatedEvent* event);

  // Set child views for |target_state|.
  void SetChildViewsForStateTransition(AppListViewState target_state);

  // Records the state transition for UMA.
  void RecordStateTransitionForUma(AppListViewState new_state);

  // Creates an Accessibility Event if the state transition warrants one.
  void MaybeCreateAccessibilityEvent(AppListViewState new_state);

  // Ensures that the app list widget bounds are set to the preferred bounds for
  // the current app list view state - intended to be called when the
  // display bounds available to the app list view change.
  void EnsureWidgetBoundsMatchCurrentState();

  // Gets the display nearest to the parent window.
  display::Display GetDisplayNearestView() const;

  // Gets the apps container view owned by this view.
  AppsContainerView* GetAppsContainerView();

  // Gets the root apps grid view owned by this view.
  PagedAppsGridView* GetRootAppsGridView();

  // Overridden from views::WidgetDelegateView:
  views::View* GetInitiallyFocusedView() override;

  // Returns true if scroll events should be ignored.
  bool ShouldIgnoreScrollEvents();

  // Returns preferred fullscreen widget bounds in parent window. Note that this
  // function should only be called after the widget is initialized.
  gfx::Rect GetPreferredWidgetBounds();

  // Reset the subpixel position offset of the |layer| so that it's DP origin
  // is snapped.
  void ResetSubpixelPositionOffset(ui::Layer* layer);

  const raw_ptr<AppListViewDelegate> delegate_;

  // Keeps track of the number of locks that prevent the app list view
  // from creating app list transition accessibility events. This is used to
  // prevent A11Y announcements when showing the assistant UI.
  int accessibility_event_disablers_ = 0;
  raw_ptr<AppListMainView, DanglingUntriaged> app_list_main_view_ = nullptr;

  raw_ptr<SearchBoxView, DanglingUntriaged> search_box_view_ =
      nullptr;  // Owned by views hierarchy.

  // The time the AppListView was requested to be shown. Used for metrics.
  std::optional<base::Time> time_shown_;

  // Whether the view is being built.
  bool is_building_ = false;

  // The state of the app list, controlled via SetState().
  AppListViewState app_list_state_ = AppListViewState::kClosed;
  // Set to target app list state while `SetState()` is being handled.
  AppListViewState target_app_list_state_ = AppListViewState::kClosed;

  // Metric reporter for state change animations.
  const std::unique_ptr<StateAnimationMetricsReporter>
      state_animation_metrics_reporter_;

  // Whether the on-screen keyboard is shown.
  bool onscreen_keyboard_shown_ = false;

  // Whether the app list has been translated up to ensure app list folder
  // view header is visible when onscreen keyboard is shown.
  bool offset_to_show_folder_with_onscreen_keyboard_ = false;

  // Used for announcing accessibility alerts.
  std::unique_ptr<AppListA11yAnnouncer> a11y_announcer_;

  // If true, the contents view is not reset when showing the app list.
  bool disable_contents_reset_when_showing_ = false;

  // A timer which will reset the app list to the initial page. This timer only
  // goes off when the app list is not visible after a set amount of time.
  base::OneShotTimer page_reset_timer_;

  // Used to cancel in progress `SetState()` request if `SetState()` gets called
  // again. Updating children state during app list view state update may cause
  // `SetState()` to get called again - for example, if exiting search results
  // page causes virtual keyboard to get hidden, and work area bounds available
  // to the app list change.
  // When calling methods that may cause nested `SetState()` call, `SetState()`
  // will bind a weak ptr to this factory, and it will bail out early if it
  // detects that `SetState()` got called again (in which case the weak ptr will
  // be invalidated).
  base::WeakPtrFactory<AppListView> set_state_weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_APP_LIST_VIEWS_APP_LIST_VIEW_H_