blob: 723b95f12a772f066431e03611b5a8095d4013fb [file] [log] [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_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_PANEL_H_
#define ASH_SYSTEM_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_PANEL_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list_types.h"
#include "chromeos/crosapi/mojom/video_conference.mojom-forward.h"
#include "ui/compositor/throughput_tracker.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/animation/animation_delegate_views.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
namespace base {
class UnguessableToken;
} // namespace base
namespace gfx {
class Animation;
class LinearAnimation;
} // namespace gfx
namespace views {
class ImageView;
class Label;
class View;
} // namespace views
namespace ash::video_conference {
class ReturnToAppPanel;
using MediaApps = std::vector<crosapi::mojom::VideoConferenceMediaAppInfoPtr>;
// The "return to app" button that resides within the "return to app" panel,
// showing information of a particular running media app. Clicking on this
// button will take users to the app.
class ASH_EXPORT ReturnToAppButton : public views::Button {
public:
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
// Called when the expanded state is changed.
virtual void OnExpandedStateChanged(bool expanded) = 0;
};
// `is_top_row` specifies if the button is in the top row of `panel`. If the
// button is in the top row, it might represent the only media app running or
// the summary row if there are multiple media apps.
ReturnToAppButton(ReturnToAppPanel* panel,
bool is_top_row,
const base::UnguessableToken& id,
bool is_capturing_camera,
bool is_capturing_microphone,
bool is_capturing_screen,
const std::u16string& display_text,
crosapi::mojom::VideoConferenceAppType app_type);
ReturnToAppButton(const ReturnToAppButton&) = delete;
ReturnToAppButton& operator=(const ReturnToAppButton&) = delete;
~ReturnToAppButton() override;
// Observer functions.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
bool is_capturing_camera() const { return is_capturing_camera_; }
bool is_capturing_microphone() const { return is_capturing_microphone_; }
bool is_capturing_screen() const { return is_capturing_screen_; }
bool expanded() const { return expanded_; }
views::Label* label() { return label_; }
views::View* icons_container() { return icons_container_; }
views::ImageView* expand_indicator() { return expand_indicator_; }
private:
FRIEND_TEST_ALL_PREFIXES(ReturnToAppPanelTest, ExpandCollapse);
// Callback for the button.
void OnButtonClicked(const base::UnguessableToken& id,
crosapi::mojom::VideoConferenceAppType app_type);
// Get the text regarding the peripherals part of the return to app button
// accessible name.
std::u16string GetPeripheralsAccessibleName();
// Indicates if the running app is using camera, microphone, or screen
// sharing.
const bool is_capturing_camera_;
const bool is_capturing_microphone_;
const bool is_capturing_screen_;
// Registered observers.
base::ObserverList<Observer> observer_list_;
// Indicates if this button (and also the parent panel) is in the expanded
// state. Note that `expanded_` is only meaningful in the case that the button
// is in the top row.
bool expanded_ = false;
// The pointers below are owned by the views hierarchy.
// This panel is the parent view of this button.
const raw_ptr<ReturnToAppPanel, ExperimentalAsh> panel_;
// Label showing the url or name of the running app.
raw_ptr<views::Label, ExperimentalAsh> label_ = nullptr;
// The container of icons showing the state of camera/microphone/screen
// capturing of the media app.
raw_ptr<views::View, ExperimentalAsh> icons_container_ = nullptr;
// The indicator showing if the panel is in expanded or collapsed state. Only
// available if the button is in the top row.
raw_ptr<views::ImageView, ExperimentalAsh> expand_indicator_ = nullptr;
base::WeakPtrFactory<ReturnToAppButton> weak_ptr_factory_{this};
};
// The "return to app" panel that resides in the video conference bubble. The
// user selects from a list of apps that are actively capturing audio/video
// and/or sharing the screen, and the selected app is brought to the top and
// focused.
class ASH_EXPORT ReturnToAppPanel : public views::View,
ReturnToAppButton::Observer {
public:
ReturnToAppPanel();
ReturnToAppPanel(const ReturnToAppPanel&) = delete;
ReturnToAppPanel& operator=(const ReturnToAppPanel&) = delete;
~ReturnToAppPanel() override;
// True if the container is running its expand/collapse animation.
bool IsExpandCollapseAnimationRunning();
int max_capturing_count() { return max_capturing_count_; }
private:
friend class ReturnToAppPanelTest;
friend class VideoConferenceIntegrationTest;
// The container that is the parent of all the buttons inside this panel.
// Mainly used to handle expand/collapse animation.
class ReturnToAppContainer : public views::View,
public views::AnimationDelegateViews {
public:
ReturnToAppContainer();
ReturnToAppContainer(const ReturnToAppContainer&) = delete;
ReturnToAppContainer& operator=(const ReturnToAppContainer&) = delete;
~ReturnToAppContainer() override;
// Starts the expand/collapse animation.
void StartExpandCollapseAnimation();
gfx::LinearAnimation* animation() { return animation_.get(); }
void set_height_before_animation(int height_before_animation) {
height_before_animation_ = height_before_animation;
}
void set_expanded_target(bool expanded_target) {
expanded_target_ = expanded_target;
}
private:
friend class ReturnToAppPanelTest;
// views::AnimationDelegateViews:
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
// views::View:
gfx::Size CalculatePreferredSize() const override;
// Animation used for the expand/collapse animation.
std::unique_ptr<gfx::LinearAnimation> animation_;
// Keeps track of the height of the panel before animation starts. This is
// used for the expand/collapse animation.
int height_before_animation_ = 0;
// Target expand state of the panel after the animation is completed.
bool expanded_target_ = false;
// Measure animation smoothness metrics for all the animations.
absl::optional<ui::ThroughputTracker> throughput_tracker_;
};
// ReturnToAppButton::Observer:
void OnExpandedStateChanged(bool expanded) override;
// views::View:
void ChildPreferredSizeChanged(View* child) override;
// Used by the ctor to add `ReturnToAppButton`(s) to the panel.
void AddButtonsToPanel(MediaApps apps);
// The container of the panel, which contains all the views and is used for
// setting padding and background painting. Owned by the views hierarchy.
raw_ptr<ReturnToAppContainer, ExperimentalAsh> container_view_ = nullptr;
// The view at the top of the panel, summarizing the information of all media
// apps. This pointer will be null when there's one or fewer media apps. Owned
// by the views hierarchy.
raw_ptr<ReturnToAppButton, ExperimentalAsh> summary_row_view_ = nullptr;
// Keep track the maximum number of capturing that an individual media app
// has. This number is used to make sure the icons in `ReturnToAppButton` are
// right aligned with each other.
int max_capturing_count_ = 0;
base::WeakPtrFactory<ReturnToAppPanel> weak_ptr_factory_{this};
};
} // namespace ash::video_conference
#endif // ASH_SYSTEM_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_PANEL_H_