1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189

ash / system / privacy / privacy_indicators_tray_item_view.h [blame]

// Copyright 2022 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_PRIVACY_PRIVACY_INDICATORS_TRAY_ITEM_VIEW_H_
#define ASH_SYSTEM_PRIVACY_PRIVACY_INDICATORS_TRAY_ITEM_VIEW_H_

#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/system/tray/tray_item_view.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/compositor/compositor_metrics_tracker.h"

namespace gfx {
class LinearAnimation;
}

namespace views {
class BoxLayout;
}  // namespace views

namespace ash {
class Shelf;

// A tray item which resides in the system tray, indicating to users that an app
// is currently accessing camera/microphone.
class ASH_EXPORT PrivacyIndicatorsTrayItemView : public TrayItemView,
                                                 public SessionObserver {
  METADATA_HEADER(PrivacyIndicatorsTrayItemView, TrayItemView)

 public:
  enum AnimationState {
    // No animation is running.
    kIdle,

    // `expand_animation_` is running.
    kExpand,

    // `expand_animation_` finishes but the the shrink animation hasn't started
    // yet. The view will dwell at its expanded size.
    kDwellInExpand,

    // Happens when `longer_side_shrink_animation_` already started but
    // `shorter_side_shrink_animation_` hasn't started yet.
    kOnlyLongerSideShrink,

    // Happens when both the 2 shrink animations are animating. Note that
    // `longer_side_shrink_animation_` ended before
    // `shorter_side_shrink_animation_`, and this state ends when
    // `shorter_side_shrink_animation_` ends.
    kBothSideShrink,
  };

  // This enum covers all the possible variations for the privacy indicators
  // view type that we are interested in recording metrics, specifying whether
  // camera/mic access and screen sharing icons are showing. Note to keep in
  // sync with enum `PrivacyIndicatorsType` in
  // tools/metrics/histograms/metadata/ash/enums.xml.
  enum class Type {
    kCamera = 1 << 1,
    kMicrophone = 1 << 2,
    kScreenSharing = 1 << 3,
    kCameraMicrophone = kCamera | kMicrophone,
    kCameraScreenSharing = kCamera | kScreenSharing,
    kMicrophoneScreenSharing = kMicrophone | kScreenSharing,
    kAllUsed = kCamera | kMicrophone | kScreenSharing,
    kMaxValue = kAllUsed,
  };

  explicit PrivacyIndicatorsTrayItemView(Shelf* shelf);

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

  ~PrivacyIndicatorsTrayItemView() override;

  views::ImageView* camera_icon() { return camera_icon_; }
  views::ImageView* microphone_icon() { return microphone_icon_; }

  // Called by `PrivacyIndicatorsController` to update the view according to the
  // new state of camara/microphone access. `is_new_app`, `was_camera_in_use`,
  // and `was_microphone_in_use` are the information used to determine if we
  // should perform an animation.
  void OnCameraAndMicrophoneAccessStateChanged(bool is_camera_used,
                                               bool is_microphone_used,
                                               bool is_new_app,
                                               bool was_camera_in_use,
                                               bool was_microphone_in_use);

  // Update the view according to the state of screen sharing.
  void UpdateScreenShareStatus(bool is_screen_sharing);

  // Update the view according to the shelf alignment.
  void UpdateAlignmentForShelf(Shelf* shelf);

  // Update the view's visibility based on camera/mic access and screen sharing
  // state.
  void UpdateVisibility();

 private:
  friend class PrivacyIndicatorsTrayItemViewPixelTest;
  friend class PrivacyIndicatorsTrayItemViewTest;

  // TrayItemView:
  void PerformVisibilityAnimation(bool visible) override;
  void HandleLocaleChange() override;
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  void OnThemeChanged() override;
  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
  views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
  void AnimationProgressed(const gfx::Animation* animation) override;
  void AnimationEnded(const gfx::Animation* animation) override;
  void AnimationCanceled(const gfx::Animation* animation) override;
  void ImmediatelyUpdateVisibility() override;

  // Performs a sequence of expand, dwell, and then shrink animations to notify
  // users about the usage of camera, microphone, and screen sharing.
  void PerformAnimation();

  // SessionObserver:
  void OnSessionStateChanged(session_manager::SessionState state) override;

  // Update the icons for the children views.
  void UpdateIcons();

  // Update the bounds insets based on shelf alignment.
  void UpdateBoundsInset();

  // Calculate the size of the view during shrink animation. We are calculating
  // for the longer side if `for_longer_side` is true, otherwise it is for
  // shorter side.
  int CalculateSizeDuringShrinkAnimation(bool for_longer_side) const;

  // Calculate the length of the longer size, based on `is_screen_sharing_`.
  int GetLongerSideLengthInExpandedMode() const;

  // End all 3 animations contained in this class.
  void EndAllAnimations();

  // Record the type of privacy indicators that are showing.
  void RecordPrivacyIndicatorsType();

  // Record repeated shows metric when the timer is stop.
  void RecordRepeatedShows();

  void UpdateTooltipText();

  raw_ptr<views::BoxLayout> layout_manager_ = nullptr;

  // Owned by the views hierarchy.
  raw_ptr<views::ImageView> camera_icon_ = nullptr;
  raw_ptr<views::ImageView> microphone_icon_ = nullptr;
  raw_ptr<views::ImageView> screen_share_icon_ = nullptr;

  // Keep track of the current screen sharing state.
  bool is_screen_sharing_ = false;

  // Keep track the current animation state during the multi-part animation.
  AnimationState animation_state_ = kIdle;

  // Animations for showing/expanding the view, then shrink it to be a dot.
  std::unique_ptr<gfx::LinearAnimation> expand_animation_;
  std::unique_ptr<gfx::LinearAnimation> longer_side_shrink_animation_;
  std::unique_ptr<gfx::LinearAnimation> shorter_side_shrink_animation_;

  // Timers for delaying shrink animations after `expand_animation_` is
  // completed.
  base::OneShotTimer longer_side_shrink_delay_timer_;
  base::OneShotTimer shorter_side_shrink_delay_timer_;

  // Used to record metrics of the number of shows per session.
  int count_visible_per_session_ = 0;

  // Keeps track of the last time the indicator starts showing. Used to record
  // visibility duration metrics.
  base::Time start_showing_time_;

  // Measure animation smoothness metrics for all the animations.
  std::optional<ui::ThroughputTracker> throughput_tracker_;
};

}  // namespace ash

#endif  // ASH_SYSTEM_PRIVACY_PRIVACY_INDICATORS_TRAY_ITEM_VIEW_H_