#include "ash/wm/splitview/split_view_metrics_controller.h"
#include <algorithm>
#include <vector>
#include "ash/root_window_controller.h"
#include "ash/root_window_settings.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/switchable_windows.h"
#include "ash/wm/window_positioning_utils.h"
#include "ash/wm/window_restore/window_restore_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_metrics.h"
#include "base/check_op.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "chromeos/ui/base/display_util.h"
#include "chromeos/ui/base/window_state_type.h"
#include "components/app_restore/window_info.h"
#include "components/app_restore/window_properties.h"
#include "components/prefs/pref_service.h"
#include "ui/aura/env.h"
#include "ui/display/screen.h"
#include "ui/display/tablet_state.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace {
constexpr char kSplitViewEntryPointDeviceUIModeHistogram[] =
"Ash.SplitView.EntryPoint.DeviceUIMode";
constexpr char kSplitViewEntryPointDeviceOrientationHistogram[] =
"Ash.SplitView.EntryPoint.DeviceOrientation";
constexpr char kSplitViewDeviceOrientationPrefix[] =
"Ash.SplitView.DeviceOrientation";
constexpr char kOrientationInSplitViewHistogram[] =
"Ash.SplitView.OrientationInSplitView";
constexpr char kTimeInSplitScreenClamshellHistogram[] =
"Ash.SplitView.TimeInSplitScreen.ClamshellMode";
constexpr char kTimeInSplitScreenTabletHistogram[] =
"Ash.SplitView.TimeInSplitScreen.TabletMode";
constexpr char kTimeInMultiDisplaySplitScreenClamshellHistogram[] =
"Ash.SplitView.TimeInMultiDisplaySplitScreen.ClamshellMode";
constexpr char kTimeInMultiDisplaySplitScreenTabletHistogram[] =
"Ash.SplitView.TimeInMultiDisplaySplitScreen.TabletMode";
constexpr char kSplitViewResizeWindowCountClamshellHistogram[] =
"Ash.SplitView.ResizeWindowCount.ClamshellMode";
constexpr char kSplitViewResizeWindowCountTabletHistogram[] =
"Ash.SplitView.ResizeWindowCount.TabletMode";
constexpr char kSplitViewSwapWindowCountHistogram[] =
"Ash.SplitView.SwapWindowCount";
constexpr base::TimeTicks kInvalidTime = base::TimeTicks::Max();
base::TimeTicks g_clamshell_multi_display_split_view_start_time;
base::TimeTicks g_tablet_multi_display_split_view_start_time;
int64_t g_clamshell_multi_display_split_view_time_ms;
bool IsRecordingClamshellMultiDisplaySplitView() {
return g_clamshell_multi_display_split_view_start_time != kInvalidTime;
}
bool IsRecordingTabletMultiDisplaySplitView() {
return g_tablet_multi_display_split_view_start_time != kInvalidTime;
}
int NumRootWindowsInSplitViewRecording() {
auto root_windows = Shell::GetAllRootWindows();
return std::ranges::count_if(root_windows, [](aura::Window* root_window) {
return SplitViewController::Get(root_window)
->split_view_metrics_controller()
->in_split_view_recording();
});
}
bool InTabletMode() {
return display::Screen::Get()->InTabletMode();
}
bool TopTwoVisibleWindowsBothSnapped(
const std::vector<raw_ptr<aura::Window, VectorExperimental>>& windows) {
int windows_size = windows.size();
if (windows_size < 2)
return false;
WindowState* top_snap_window_state = WindowState::Get(windows.back());
if (!top_snap_window_state->IsSnapped())
return false;
for (aura::Window* window : base::Reversed(windows)) {
if (window == windows.back())
continue;
auto* window_state = WindowState::Get(window);
if (!window->IsVisible())
continue;
if (!window_state->IsSnapped())
return false;
if (window_state->GetStateType() == top_snap_window_state->GetStateType()) {
continue;
} else {
return true;
}
}
return false;
}
std::string GetHistogramNameWithDeviceUIMode(std::string prefix) {
return prefix.append(InTabletMode() ? ".TabletMode" : ".ClamshellMode");
}
SplitViewMetricsController::DeviceOrientation GetDeviceOrientation(
const display::Display& display) {
return display.is_landscape()
? SplitViewMetricsController::DeviceOrientation::kLandscape
: SplitViewMetricsController::DeviceOrientation::kPortrait;
}
void MaybeRecordSnapWindowSuggestions(
WindowSnapActionSource snap_action_source) {
if (!CanSnapActionSourceStartFasterSplitView(snap_action_source)) {
return;
}
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
if (!pref_service) {
return;
}
base::UmaHistogramBoolean(
BuildSnapWindowSuggestionsHistogramName(snap_action_source),
pref_service->GetBoolean(prefs::kSnapWindowSuggestions));
}
}
SplitViewMetricsController* SplitViewMetricsController::Get(
aura::Window* window) {
DCHECK(window);
DCHECK(window->GetRootWindow());
auto* root_window_controller = RootWindowController::ForWindow(window);
DCHECK(root_window_controller);
return root_window_controller->split_view_controller()
->split_view_metrics_controller();
}
SplitViewMetricsController::SplitViewMetricsController(
SplitViewController* split_view_controller)
: split_view_controller_(split_view_controller) {
split_view_controller_->AddObserver(this);
Shell::Get()->activation_client()->AddObserver(this);
auto* desks_controller = Shell::Get()->desks_controller();
desks_controller->AddObserver(this);
current_desk_ = desks_controller->active_desk();
aura::Env::GetInstance()->AddObserver(this);
const display::Display display =
display::Screen::Get()->GetDisplayNearestWindow(
split_view_controller->root_window());
orientation_ = GetDeviceOrientation(display);
ResetTimeAndCounter();
}
SplitViewMetricsController::~SplitViewMetricsController() {
ClearObservedWindows();
split_view_controller_->RemoveObserver(this);
Shell::Get()->activation_client()->RemoveObserver(this);
Shell::Get()->desks_controller()->RemoveObserver(this);
aura::Env::GetInstance()->RemoveObserver(this);
}
void SplitViewMetricsController::OnSplitViewStateChanged(
SplitViewController::State previous_state,
SplitViewController::State state) {
if (previous_state == state)
return;
if (previous_state == SplitViewController::State::kNoSnap)
StartRecordSplitViewMetrics();
else if (state == SplitViewController::State::kNoSnap)
StopRecordSplitViewMetrics();
}
void SplitViewMetricsController::OnSplitViewWindowResized() {
DCHECK(split_view_controller_->InSplitViewMode());
if (split_view_controller_->InClamshellSplitViewMode())
clamshell_resize_count_ += 1;
else
tablet_resize_count_ += 1;
}
void SplitViewMetricsController::OnSplitViewWindowSwapped() {
DCHECK(split_view_controller_->InSplitViewMode());
swap_count_ += 1;
if (split_view_controller_->InClamshellSplitViewMode())
clamshell_resize_count_ -= 2;
else
tablet_resize_count_ -= 2;
}
void SplitViewMetricsController::OnDisplayMetricsChanged(
const display::Display& display,
uint32_t changed_metrics) {
if (!(changed_metrics &
display::DisplayObserver::DisplayMetric::DISPLAY_METRIC_ROTATION)) {
return;
}
if (GetRootWindowSettings(split_view_controller_->root_window())
->display_id != display.id()) {
return;
}
const DeviceOrientation orientation = GetDeviceOrientation(display);
if (orientation_ == orientation)
return;
orientation_ = orientation;
if (in_split_view_recording_) {
base::UmaHistogramEnumeration(kOrientationInSplitViewHistogram,
orientation_);
ReportDeviceUIModeAndOrientationHistogram();
}
}
void SplitViewMetricsController::OnDisplayTabletStateChanged(
display::TabletState state) {
switch (state) {
case display::TabletState::kEnteringTabletMode:
case display::TabletState::kExitingTabletMode:
break;
case display::TabletState::kInTabletMode:
OnTabletModeStarted();
break;
case display::TabletState::kInClamshellMode:
OnTabletModeEnded();
break;
}
}
void SplitViewMetricsController::OnWindowParentChanged(aura::Window* window,
aura::Window* parent) {
if (parent && desks_util::IsDeskContainer(parent)) {
if (parent->GetId() != current_desk_->container_id()) {
RemoveObservedWindow(window);
} else if (base::Contains(no_state_observed_windows_, window)) {
WindowState::Get(window)->AddObserver(this);
no_state_observed_windows_.erase(window);
}
MaybeStartOrEndRecordBothSnappedClamshellSplitView();
}
}
void SplitViewMetricsController::OnResizeLoopEnded(aura::Window* window) {
if (!in_split_view_recording_ || split_view_controller_->InSplitViewMode())
return;
clamshell_resize_count_ += 1;
}
void SplitViewMetricsController::OnWindowDestroyed(aura::Window* window) {
RemoveObservedWindow(window);
MaybeStartOrEndRecordBothSnappedClamshellSplitView();
}
void SplitViewMetricsController::OnWindowRemovingFromRootWindow(
aura::Window* window,
aura::Window* new_root) {
RemoveObservedWindow(window);
MaybeStartOrEndRecordBothSnappedClamshellSplitView();
if (new_root) {
auto* target_split_view_metrics_controller =
SplitViewController::Get(new_root)->split_view_metrics_controller();
DCHECK(target_split_view_metrics_controller);
if (!target_split_view_metrics_controller->IsObservingWindow(window)) {
target_split_view_metrics_controller->AddObservedWindow(window);
target_split_view_metrics_controller
->MaybeStartOrEndRecordBothSnappedClamshellSplitView();
}
}
}
void SplitViewMetricsController::OnPostWindowStateTypeChange(
WindowState* window_state,
chromeos::WindowStateType old_type) {
MaybeStartOrEndRecordSnapTwoWindowsDuration(window_state);
MaybeStartOrEndRecordMinimizeTwoWindowsDuration(window_state, old_type);
bool is_snapped = window_state->IsSnapped();
if (is_snapped) {
MaybeRecordSnapWindowSuggestions(
window_state->snap_action_source().value_or(
WindowSnapActionSource::kNotSpecified));
}
bool was_snapped = chromeos::IsSnappedWindowStateType(old_type);
if (is_snapped == was_snapped)
return;
if (was_snapped &&
chromeos::IsSnappedWindowStateType(first_closed_state_type_) &&
old_type != first_closed_state_type_) {
RecordCloseTwoWindowsDuration(kSequentialSnapActionMaxTime);
}
MaybeStartOrEndRecordBothSnappedClamshellSplitView();
}
void SplitViewMetricsController::OnWindowActivated(ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) {
AddOrStackWindowOnTop(gained_active);
MaybeStartOrEndRecordBothSnappedClamshellSplitView();
}
void SplitViewMetricsController::OnDeskActivationChanged(
const Desk* activated,
const Desk* deactivated) {
StopRecordSplitViewMetrics();
current_desk_ = Shell::Get()->desks_controller()->active_desk();
InitObservedWindowsOnActiveDesk();
MaybeStartOrEndRecordBothSnappedClamshellSplitView();
}
void SplitViewMetricsController::OnWindowInitialized(aura::Window* window) {
int32_t* activation_index =
window->GetProperty(app_restore::kActivationIndexKey);
if (!activation_index)
return;
app_restore::WindowInfo* window_info =
window->GetProperty(app_restore::kWindowInfoKey);
if (!window_info)
return;
if (!window_info->current_bounds.has_value() ||
!display::Screen::Get()
->GetDisplayNearestWindow(split_view_controller_->root_window())
.work_area()
.Contains(window_info->current_bounds.value())) {
return;
}
if (!window_info->desk_guid.is_valid() ||
window_info->desk_guid != current_desk_->uuid()) {
return;
}
window->AddObserver(this);
no_state_observed_windows_.insert(window);
observed_windows_.insert(WindowRestoreController::GetWindowToInsertBefore(
window, observed_windows_),
window);
}
void SplitViewMetricsController::StartRecordSplitViewMetrics() {
if (in_split_view_recording_)
return;
if (split_view_controller_->InClamshellSplitViewMode() &&
Shell::Get()
->mru_window_tracker()
->BuildMruWindowList(DesksMruType::kActiveDesk)
.size() == 1) {
return;
}
bool in_clamshell = !InTabletMode();
if (in_clamshell)
StartRecordClamshellSplitView();
else
StartRecordTabletSplitView();
in_split_view_recording_ = true;
if (NumRootWindowsInSplitViewRecording() == 2) {
if (in_clamshell)
StartRecordClamshellMultiDisplaySplitView();
else
StartRecordTabletMultiDisplaySplitView();
}
base::UmaHistogramEnumeration(kSplitViewEntryPointDeviceOrientationHistogram,
orientation_);
base::UmaHistogramEnumeration(
kSplitViewEntryPointDeviceUIModeHistogram,
in_clamshell ? DeviceUIMode::kClamshell : DeviceUIMode::kTablet);
}
void SplitViewMetricsController::StopRecordSplitViewMetrics() {
if (!in_split_view_recording_)
return;
bool is_recording_clamshell_metrics = IsRecordingClamshellMetrics();
if (is_recording_clamshell_metrics) {
if (auto* to_be_activated_window =
split_view_controller_->to_be_activated_window()) {
AddOrStackWindowOnTop(to_be_activated_window);
}
if (TopTwoVisibleWindowsBothSnapped(observed_windows_))
return;
StopRecordClamshellSplitView();
} else {
StopRecordTabletSplitView();
}
base::UmaHistogramCounts100(kSplitViewSwapWindowCountHistogram, swap_count_);
in_split_view_recording_ = false;
if (NumRootWindowsInSplitViewRecording() == 1) {
if (is_recording_clamshell_metrics)
StopRecordClamshellMultiDisplaySplitView();
else
StopRecordTabletMultiDisplaySplitView();
}
ResetTimeAndCounter();
}
bool SplitViewMetricsController::IsObservingWindow(aura::Window* window) const {
return base::Contains(observed_windows_, window);
}
void SplitViewMetricsController::AddObservedWindow(aura::Window* window) {
window->AddObserver(this);
WindowState::Get(window)->AddObserver(this);
observed_windows_.emplace_back(window);
}
void SplitViewMetricsController::RemoveObservedWindow(aura::Window* window) {
if (window->is_destroying()) {
MaybeStartOrEndRecordCloseTwoWindowsDuration(window);
}
if (window == first_snapped_window_) {
if (window->is_destroying()) {
RecordSnapTwoWindowsDuration(kSequentialSnapActionMaxTime);
}
first_snapped_window_ = nullptr;
}
if (first_minimized_window_state_ &&
window == first_minimized_window_state_->window()) {
if (window->is_destroying()) {
RecordMinimizeTwoWindowsDuration(kSequentialSnapActionMaxTime);
}
first_minimized_window_state_ = nullptr;
}
if (std::erase(observed_windows_, window)) {
WindowState::Get(window)->RemoveObserver(this);
window->RemoveObserver(this);
}
}
void SplitViewMetricsController::AddOrStackWindowOnTop(aura::Window* window) {
if (!window)
return;
if (window->GetRootWindow() != split_view_controller_->root_window())
return;
aura::Window* parent = window->parent();
if (!parent || !IsSwitchableContainer(parent))
return;
const int parent_id = parent->GetId();
if (desks_util::IsDeskContainerId(parent_id) &&
parent_id != current_desk_->container_id()) {
return;
}
if (!CanIncludeWindowInMruList(window))
return;
auto iter = std::ranges::find(observed_windows_, window);
if (iter == observed_windows_.end()) {
AddObservedWindow(window);
} else {
observed_windows_.erase(iter);
observed_windows_.emplace_back(window);
}
}
void SplitViewMetricsController::InitObservedWindowsOnActiveDesk() {
ClearObservedWindows();
auto windows =
current_desk_
->GetDeskContainerForRoot(split_view_controller_->root_window())
->children();
for (aura::Window* window : windows) {
if (!CanIncludeWindowInMruList(window))
continue;
AddObservedWindow(window);
}
}
void SplitViewMetricsController::ClearObservedWindows() {
while (!observed_windows_.empty()) {
RemoveObservedWindow(observed_windows_.back());
}
}
void SplitViewMetricsController::
MaybeStartOrEndRecordBothSnappedClamshellSplitView() {
if (InTabletMode() || split_view_controller_->InSplitViewMode()) {
return;
}
bool both_snapped = TopTwoVisibleWindowsBothSnapped(observed_windows_);
if (!in_split_view_recording_ && both_snapped) {
StartRecordSplitViewMetrics();
} else if (in_split_view_recording_ && !both_snapped) {
StopRecordSplitViewMetrics();
}
}
bool SplitViewMetricsController::
MaybePauseRecordBothSnappedClamshellSplitView() {
if (InTabletMode() || split_view_controller_->InSplitViewMode()) {
return false;
}
if (observed_windows_.size() < 3)
return false;
auto iter = observed_windows_.end() - 1;
auto begin_iter = observed_windows_.begin();
for (; iter != begin_iter; iter--) {
if (!(*iter)->IsVisible())
continue;
if (WindowState::Get(*iter)->IsSnapped())
return false;
else
break;
}
if (iter == begin_iter)
return false;
return TopTwoVisibleWindowsBothSnapped(
std::vector<raw_ptr<aura::Window, VectorExperimental>>(begin_iter, iter));
}
void SplitViewMetricsController::RecordSnapTwoWindowsDuration(
const base::TimeDelta& elapsed_time) {
base::UmaHistogramCustomTimes(kSnapTwoWindowsDurationHistogramName,
elapsed_time,
kSequentialSnapActionMinTime,
kSequentialSnapActionMaxTime, 100);
first_snapped_window_ = nullptr;
first_snapped_time_ = base::TimeTicks();
}
void SplitViewMetricsController::RecordMinimizeTwoWindowsDuration(
const base::TimeDelta& elapsed_time) {
base::UmaHistogramCustomTimes(kMinimizeTwoWindowsDurationHistogramName,
elapsed_time,
kSequentialSnapActionMinTime,
kSequentialSnapActionMaxTime, 100);
first_minimized_window_state_ = nullptr;
first_minimized_time_ = base::TimeTicks();
}
void SplitViewMetricsController::RecordCloseTwoWindowsDuration(
const base::TimeDelta& elapsed_time) {
base::UmaHistogramCustomTimes(kCloseTwoWindowsDurationHistogramName,
elapsed_time,
kSequentialSnapActionMinTime,
kSequentialSnapActionMaxTime, 100);
first_closed_state_type_ = chromeos::WindowStateType::kDefault;
first_closed_time_ = base::TimeTicks();
}
void SplitViewMetricsController::MaybeStartOrEndRecordSnapTwoWindowsDuration(
WindowState* window_state) {
if (first_snapped_window_ &&
!WindowState::Get(first_snapped_window_)->IsSnapped()) {
RecordSnapTwoWindowsDuration(kSequentialSnapActionMaxTime);
}
if (window_state->IsSnapped()) {
if (first_snapped_window_ && !first_snapped_time_.is_null() &&
window_state->window() != first_snapped_window_ &&
window_state->GetStateType() ==
ToWindowStateType(GetOppositeSnapType(first_snapped_window_))) {
RecordSnapTwoWindowsDuration(base::TimeTicks::Now() -
first_snapped_time_);
return;
}
first_snapped_window_ = window_state->window();
first_snapped_time_ = base::TimeTicks::Now();
return;
}
}
void SplitViewMetricsController::
MaybeStartOrEndRecordMinimizeTwoWindowsDuration(
WindowState* window_state,
chromeos::WindowStateType old_type) {
const bool is_minimized = window_state->IsMinimized();
if (is_minimized && chromeos::IsSnappedWindowStateType(old_type)) {
if (first_minimized_window_state_ && !first_minimized_time_.is_null()) {
RecordMinimizeTwoWindowsDuration(base::TimeTicks::Now() -
first_minimized_time_);
return;
}
first_minimized_window_state_ = window_state;
first_minimized_time_ = base::TimeTicks::Now();
return;
}
if (window_state == first_minimized_window_state_ && !is_minimized &&
!window_state->IsSnapped()) {
RecordMinimizeTwoWindowsDuration(kSequentialSnapActionMaxTime);
}
}
void SplitViewMetricsController::MaybeStartOrEndRecordCloseTwoWindowsDuration(
aura::Window* window) {
if (auto* window_state = WindowState::Get(window);
window_state && window_state->IsSnapped()) {
if (!chromeos::IsSnappedWindowStateType(first_closed_state_type_)) {
first_closed_state_type_ = window_state->GetStateType();
first_closed_time_ = base::TimeTicks::Now();
return;
}
if (ToWindowStateType(GetOppositeSnapType(window)) ==
first_closed_state_type_ &&
!first_closed_time_.is_null()) {
RecordCloseTwoWindowsDuration(base::TimeTicks::Now() -
first_closed_time_);
}
}
}
void SplitViewMetricsController::ResetTimeAndCounter() {
clamshell_split_view_start_time_ = kInvalidTime;
tablet_split_view_start_time_ = kInvalidTime;
clamshell_resize_count_ = 0;
tablet_resize_count_ = 0;
swap_count_ = 0;
}
void SplitViewMetricsController::OnTabletModeStarted() {
if (in_split_view_recording_ && IsRecordingClamshellMetrics()) {
StopRecordClamshellSplitView();
StartRecordTabletSplitView();
if (NumRootWindowsInSplitViewRecording() > 1 &&
IsRecordingClamshellMultiDisplaySplitView()) {
StopRecordClamshellMultiDisplaySplitView();
StartRecordTabletMultiDisplaySplitView();
}
}
}
void SplitViewMetricsController::OnTabletModeEnded() {
if (in_split_view_recording_ && IsRecordingTabletMetrics()) {
StopRecordTabletSplitView();
StartRecordClamshellSplitView();
if (NumRootWindowsInSplitViewRecording() > 1 &&
IsRecordingTabletMultiDisplaySplitView()) {
StopRecordTabletMultiDisplaySplitView();
StartRecordClamshellMultiDisplaySplitView();
}
}
}
bool SplitViewMetricsController::IsRecordingClamshellMetrics() const {
return clamshell_split_view_start_time_ != kInvalidTime;
}
bool SplitViewMetricsController::IsRecordingTabletMetrics() const {
return tablet_split_view_start_time_ != kInvalidTime;
}
void SplitViewMetricsController::StartRecordClamshellSplitView() {
clamshell_split_view_start_time_ = base::TimeTicks::Now();
ReportDeviceUIModeAndOrientationHistogram();
}
void SplitViewMetricsController::StopRecordClamshellSplitView() {
DCHECK_NE(clamshell_split_view_start_time_, kInvalidTime);
clamshell_split_view_time_ +=
(base::TimeTicks::Now() - clamshell_split_view_start_time_)
.InMicroseconds();
clamshell_split_view_start_time_ = kInvalidTime;
if (MaybePauseRecordBothSnappedClamshellSplitView())
return;
base::UmaHistogramLongTimes(kTimeInSplitScreenClamshellHistogram,
base::Milliseconds(clamshell_split_view_time_));
base::UmaHistogramCounts100(kSplitViewResizeWindowCountClamshellHistogram,
clamshell_resize_count_);
clamshell_split_view_time_ = 0;
clamshell_resize_count_ = 0;
}
void SplitViewMetricsController::StartRecordTabletSplitView() {
tablet_split_view_start_time_ = base::TimeTicks::Now();
ReportDeviceUIModeAndOrientationHistogram();
}
void SplitViewMetricsController::StopRecordTabletSplitView() {
DCHECK_NE(tablet_split_view_start_time_, kInvalidTime);
base::UmaHistogramLongTimes(
kTimeInSplitScreenTabletHistogram,
base::TimeTicks::Now() - tablet_split_view_start_time_);
tablet_split_view_start_time_ = kInvalidTime;
base::UmaHistogramCounts100(kSplitViewResizeWindowCountTabletHistogram,
tablet_resize_count_);
tablet_resize_count_ = 0;
}
void SplitViewMetricsController::StartRecordClamshellMultiDisplaySplitView() {
g_clamshell_multi_display_split_view_start_time = base::TimeTicks::Now();
ReportDeviceUIModeAndOrientationHistogram();
}
void SplitViewMetricsController::StopRecordClamshellMultiDisplaySplitView() {
DCHECK_NE(g_clamshell_multi_display_split_view_start_time, kInvalidTime);
g_clamshell_multi_display_split_view_time_ms =
(base::TimeTicks::Now() - g_clamshell_multi_display_split_view_start_time)
.InMilliseconds();
g_clamshell_multi_display_split_view_start_time = kInvalidTime;
if (MaybePauseRecordBothSnappedClamshellSplitView())
return;
base::UmaHistogramLongTimes(
kTimeInMultiDisplaySplitScreenClamshellHistogram,
base::Milliseconds(g_clamshell_multi_display_split_view_time_ms));
g_clamshell_multi_display_split_view_time_ms = 0;
}
void SplitViewMetricsController::StartRecordTabletMultiDisplaySplitView() {
g_tablet_multi_display_split_view_start_time = base::TimeTicks::Now();
ReportDeviceUIModeAndOrientationHistogram();
}
void SplitViewMetricsController::StopRecordTabletMultiDisplaySplitView() {
DCHECK_NE(g_tablet_multi_display_split_view_start_time, kInvalidTime);
base::UmaHistogramLongTimes(
kTimeInMultiDisplaySplitScreenTabletHistogram,
base::TimeTicks::Now() - g_tablet_multi_display_split_view_start_time);
g_tablet_multi_display_split_view_start_time = kInvalidTime;
}
void SplitViewMetricsController::ReportDeviceUIModeAndOrientationHistogram() {
base::UmaHistogramEnumeration(
GetHistogramNameWithDeviceUIMode(kSplitViewDeviceOrientationPrefix),
orientation_);
}
}