chromium/ash/system/focus_mode/focus_mode_tray.h

// Copyright 2023 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_SYSTEM_FOCUS_MODE_FOCUS_MODE_TRAY_H_
#define ASH_SYSTEM_FOCUS_MODE_FOCUS_MODE_TRAY_H_

#include "ash/ash_export.h"
#include "ash/system/focus_mode/focus_mode_controller.h"
#include "ash/system/focus_mode/focus_mode_countdown_view.h"
#include "ash/system/focus_mode/focus_mode_ending_moment_view.h"
#include "ash/system/focus_mode/focus_mode_tasks_model.h"
#include "ash/system/tray/tray_background_view.h"
#include "base/scoped_observation.h"
#include "ui/base/metadata/metadata_header_macros.h"

namespace views {
class ImageButton;
}

namespace ash {

class ProgressIndicator;
class TrayBubbleWrapper;

// Status area tray which is visible when focus mode is enabled. A circular
// progress bar is displayed around the tray displaying how much time is left in
// the focus session. The tray also controls a bubble that is shown when the
// button is clicked.
class ASH_EXPORT FocusModeTray : public TrayBackgroundView,
                                 public FocusModeController::Observer,
                                 public FocusModeTasksModel::Observer {
  METADATA_HEADER(FocusModeTray, TrayBackgroundView)

 public:
  explicit FocusModeTray(Shelf* shelf);
  FocusModeTray(const FocusModeTray&) = delete;
  FocusModeTray& operator=(const FocusModeTray&) = delete;
  ~FocusModeTray() override;

  // TrayBackgroundView:
  void ClickedOutsideBubble(const ui::LocatedEvent& event) override;
  std::u16string GetAccessibleNameForTray() override;
  std::u16string GetAccessibleNameForBubble() override;
  void HandleLocaleChange() override {}
  void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
  void HideBubble(const TrayBubbleView* bubble_view) override;
  TrayBubbleView* GetBubbleView() override;
  void CloseBubbleInternal() override;
  void ShowBubble() override;
  void UpdateTrayItemColor(bool is_active) override;
  void OnThemeChanged() override;
  void OnAnimationEnded() override;

  // FocusModeController::Observer:
  void OnFocusModeChanged(bool in_focus_session) override;
  void OnTimerTick(const FocusModeSession::Snapshot& session_snapshot) override;
  void OnActiveSessionDurationChanged(
      const FocusModeSession::Snapshot& session_snapshot) override;

  // FocusModeTasksModel::Observer:
  void OnSelectedTaskChanged(const std::optional<FocusModeTask>& task) override;
  void OnTasksUpdated(const std::vector<FocusModeTask>& tasks) override;
  void OnTaskCompleted(const FocusModeTask& completed_task) override;

  // views::View:
  void Layout(PassKey) override;

  views::ImageView* image_view() { return image_view_; }

  FocusModeCountdownView* countdown_view_for_testing() {
    return countdown_view_;
  }
  FocusModeEndingMomentView* ending_moment_view_for_testing() {
    return ending_moment_view_;
  }
  const views::ImageButton* GetRadioButtonForTesting() const;
  const views::Label* GetTaskTitleForTesting() const;

 private:
  friend class FocusModeTrayTest;

  // TODO(b/314022131): Move `TaskItemView` to its own files.
  class TaskItemView;

  // Helper function for creating and setting up the `TaskItemView`.
  void CreateTaskItemView(const std::string& task_title);

  // Updates the image and color of the icon.
  void UpdateTrayIcon();

  // Button click handler for shelf icon.
  void FocusModeIconActivated(const ui::Event& event);

  void UpdateBubbleViews(const FocusModeSession::Snapshot& session_snapshot);

  // Calls `UpdateUI` on `countdown_view_` if it exists and is visible.
  void MaybeUpdateCountdownViewUI(
      const FocusModeSession::Snapshot& session_snapshot);

  // Calls `UpdateUI` on `ending_moment_view_` if it exists and is visible.
  void MaybeUpdateEndingMomentViewUI(
      const FocusModeSession::Snapshot& session_snapshot);

  // Called when the user clicks the radio button to mark a selected task as
  // completed, or if the task is already completed when we show the bubble.
  // `update` is used to determine if we need to update the tasks provider (i.e.
  // we don't if the task is already marked as completed).
  void HandleCompleteTaskButton();

  // Perform the UI update to dismiss the task view.
  void OnClearTask();

  // Called when the animation in `AnimateBubbleResize` starts.
  void OnBubbleResizeAnimationStarted();
  // Called when the animation in `AnimateBubbleResize` ends.
  void OnBubbleResizeAnimationEnded();
  // Animates resizing the bubble view after `task_item_view_` has been removed
  // from the bubble.
  void AnimateBubbleResize();
  // Updates the progression of the progress indicator.
  void UpdateProgressRing();

  // Returns whether the event is located specifically on this focus mode tray
  // view.
  bool EventTargetsTray(const ui::LocatedEvent& event) const;

  // Handles all the cleanup logic associated with closing the bubble, and may
  // reset the focus session if conditions are met. This helper function is
  // mainly used to prevent resetting the focus session when using
  // multi-displays during the ending moment.
  void CloseBubbleAndMaybeReset(bool should_reset);

  // This is used to track the current session snapshot, if any.
  std::optional<FocusModeSession::Snapshot> session_snapshot_;

  // Image view of the focus mode lamp.
  const raw_ptr<views::ImageView> image_view_;

  // The main content view of the bubble.
  raw_ptr<FocusModeCountdownView> countdown_view_ = nullptr;
  raw_ptr<FocusModeEndingMomentView> ending_moment_view_ = nullptr;

  // A box layout view which has a radio/check icon and a label for a selected
  // task.
  raw_ptr<TaskItemView> task_item_view_ = nullptr;

  // `TaskId` of the selected task shown in the `task_item_view_` if it exists.
  std::optional<TaskId> selected_task_;

  raw_ptr<views::BoxLayoutView> bubble_view_container_ = nullptr;

  // The bubble that appears after clicking the tray button.
  std::unique_ptr<TrayBubbleWrapper> bubble_;

  // An object that draws and updates the progress ring.
  std::unique_ptr<ProgressIndicator> progress_indicator_;

  // True to show the progress ring after the pulse animation of
  // `progress_indicator_`.
  bool show_progress_ring_after_animation_ = false;

  // True when the bounce in animation of the tray is done during the ending
  // moment; it will be reset to false when starting or ending a focus session,
  // or extending a focus session during the ending moment.
  bool bounce_in_animation_finished_ = false;

  // The percentage threshold that the session progress needs to exceed in order
  // to trigger a progress ring paint.
  double progress_ring_update_threshold_ = 0.0;

  base::ScopedObservation<FocusModeTasksModel, FocusModeTasksModel::Observer>
      tasks_observation_{this};

  base::WeakPtrFactory<FocusModeTray> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_SYSTEM_FOCUS_MODE_FOCUS_MODE_TRAY_H_