chromium/ash/system/audio/audio_detailed_view.h

// Copyright 2014 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_AUDIO_AUDIO_DETAILED_VIEW_H_
#define ASH_SYSTEM_AUDIO_AUDIO_DETAILED_VIEW_H_

#include <cstdint>
#include <map>
#include <memory>
#include <optional>

#include "ash/accessibility/accessibility_observer.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_controller.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/style/switch.h"
#include "ash/system/audio/audio_detailed_view_utils.h"
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/tray_detailed_view.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/ash/components/audio/audio_device.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/soda/soda_installer.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/view.h"

namespace gfx {
struct VectorIcon;
}

namespace ash {
class MicGainSliderController;
class LabeledSliderView;
class UnifiedAudioDetailedViewControllerSodaTest;
class UnifiedAudioDetailedViewControllerTest;
class UnifiedVolumeSliderController;

class ASH_EXPORT AudioDetailedView
    : public AccessibilityObserver,
      public apps::AppCapabilityAccessCache::Observer,
      public CrasAudioHandler::AudioObserver,
      public SessionObserver,
      public speech::SodaInstaller::Observer,
      public TrayDetailedView {
  METADATA_HEADER(AudioDetailedView, TrayDetailedView)

 public:
  explicit AudioDetailedView(DetailedViewDelegate* delegate);

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

  ~AudioDetailedView() override;

  // IDs used for the views that compose the Audio UI.
  // Note that these IDs are only guaranteed to be unique inside
  // `AudioDetailedView`.
  enum AudioDetailedViewID {
    // Starts at 1000 to prevent potential overlapping.
    kAudioDetailedView = 1000,
    // Agc information row and corresponding text label.
    kAgcInfoLabel,
    kAgcInfoView,
    // Warning message view when an NBS device is selected.
    kNbsWarningView,
  };

  using NoiseCancellationCallback =
      base::RepeatingCallback<void(uint64_t, views::View*)>;
  static void SetMapNoiseCancellationToggleCallbackForTest(
      NoiseCancellationCallback* map_noise_cancellation_toggle_callback);

  using StyleTransferCallback =
      base::RepeatingCallback<void(uint64_t, views::View*)>;
  static void SetMapStyleTransferToggleCallbackForTest(
      StyleTransferCallback* map_style_transfer_toggle_callback);

  views::View* GetAsView();

  // Updates the `AudioDetailedView` and re-layout.
  void Update();

  // AccessibilityObserver:
  void OnAccessibilityStatusChanged() override;

  // apps::AppCapabilityAccessCache::Observer:
  void OnCapabilityAccessUpdate(
      const apps::CapabilityAccessUpdate& update) override;
  void OnAppCapabilityAccessCacheWillBeDestroyed(
      apps::AppCapabilityAccessCache* cache) override;

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

  // CrasAudioHandler::AudioObserver:
  void OnNumStreamIgnoreUiGainsChanged(int32_t num) override;

 private:
  friend class AudioDetailedViewTest;
  friend class AudioDetailedViewAgcInfoTest;
  friend class UnifiedAudioDetailedViewControllerSodaTest;
  friend class UnifiedAudioDetailedViewControllerTest;

  // Helper function to add non-clickable header rows within the scrollable
  // list.
  void AddAudioSubHeader(views::View* container,
                         const gfx::VectorIcon& icon,
                         const int text_id);

  // Creates the items other than the devices during initialization.
  void CreateItems();

  // Creates the NBS warning view.
  std::unique_ptr<TriView> CreateNbsWarningView();

  // Creates the `live_caption_view_`.
  void CreateLiveCaptionView();

  // Creates the noise cancellation toggle row in the input subsection.
  std::unique_ptr<HoverHighlightView> CreateNoiseCancellationToggleRow(
      const AudioDevice& device);

  // Creates the style transfer toggle row in the input subsection.
  std::unique_ptr<HoverHighlightView> CreateStyleTransferToggleRow(
      const AudioDevice& device);

  // Creates the agc info row in the input subsection.
  std::unique_ptr<HoverHighlightView> CreateAgcInfoRow(
      const AudioDevice& device);

  // Creates and returns the `LabeledSliderView`. Focuses on the slider of
  // `LabeledSliderView` if `device` is focused and active.
  LabeledSliderView* CreateLabeledSliderView(views::View* container,
                                             const AudioDevice& device);

  // Sets the subtext for `live_caption_view_` based on whether live caption has
  // updated if this feature is enabled and visible in tray.
  void MaybeShowSodaMessage(speech::LanguageCode language_code,
                            std::u16string message);

  // Callback passed to the noise cancellation toggle button.
  void OnInputNoiseCancellationTogglePressed();

  // Callback passed to the style transfer toggle button.
  void OnInputStyleTransferTogglePressed();

  // Callback passed to the Settings button.
  void OnSettingsButtonClicked();

  // Toggles live caption state to trigger `AccessibilityObserver` to update the
  // UI.
  void ToggleLiveCaptionState();

  // Updates `live_caption_view_` via the UI based on `is_enabled`.
  void UpdateLiveCaptionView(bool is_enabled);

  // Updates `output_devices_` and `input_devices_`.
  void UpdateAudioDevices();

  // Adds a separator if the `device` is the last one in `input_devices_` if
  // it's an input device or is the last one in `output_devices_` if it's an
  // output device.
  void AddSeparatorIfNotLast(views::View* container, const AudioDevice& device);

  // Updates the child views in `scroll_content()`.
  void UpdateScrollableList();

  // Updates the label of AGC info when accessibility to microphone changed.
  // Hide AGC info row if no apps is requesting AGC stream.
  void UpdateAgcInfoRow();
  bool ShowAgcInfoRow();

  // TrayDetailedView:
  void HandleViewClicked(views::View* view) override;
  void CreateExtraTitleRowButtons() override;

  // SodaInstaller::Observer:
  void OnSodaInstalled(speech::LanguageCode language_code) override;
  void OnSodaInstallError(speech::LanguageCode language_code,
                          speech::SodaInstaller::ErrorCode error_code) override;
  void OnSodaProgress(speech::LanguageCode language_code,
                      int combined_progress) override;

  // CrasAudioHandler::AudioObserver:
  void OnOutputMuteChanged(bool mute_on) override;
  void OnInputMuteChanged(
      bool mute_on,
      CrasAudioHandler::InputMuteChangeMethod method) override;
  void OnInputMutedByMicrophoneMuteSwitchChanged(bool muted) override;

  std::unique_ptr<MicGainSliderController> mic_gain_controller_;
  std::unique_ptr<UnifiedVolumeSliderController>
      unified_volume_slider_controller_;
  AudioDeviceList output_devices_;
  AudioDeviceList input_devices_;
  AudioDeviceViewMap device_map_;
  std::optional<uint64_t> focused_device_id_;

  int num_stream_ignore_ui_gains_ = 0;

  // Owned by the views hierarchy.
  raw_ptr<HoverHighlightView> live_caption_view_ = nullptr;
  raw_ptr<views::ImageView> live_caption_icon_ = nullptr;
  raw_ptr<Switch> live_caption_button_ = nullptr;
  raw_ptr<HoverHighlightView> noise_cancellation_view_ = nullptr;
  raw_ptr<views::ImageView> noise_cancellation_icon_ = nullptr;
  raw_ptr<Switch> noise_cancellation_button_ = nullptr;
  raw_ptr<HoverHighlightView> style_transfer_view_ = nullptr;
  raw_ptr<views::ImageView> style_transfer_icon_ = nullptr;
  raw_ptr<Switch> style_transfer_button_ = nullptr;
  raw_ptr<views::Button> settings_button_ = nullptr;

  base::ScopedObservation<SessionController, SessionObserver>
      session_observation_{this};
  base::ScopedObservation<apps::AppCapabilityAccessCache,
                          apps::AppCapabilityAccessCache::Observer>
      app_capability_observation_{this};
  raw_ptr<apps::AppRegistryCache> app_registry_cache_;
  raw_ptr<apps::AppCapabilityAccessCache> app_capability_access_cache_;

  base::WeakPtrFactory<AudioDetailedView> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_SYSTEM_AUDIO_AUDIO_DETAILED_VIEW_H_