// 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.
#include "ash/system/time/calendar_metrics.h"
#include "ash/public/cpp/metrics_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "google_apis/common/api_error_codes.h"
#include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/events/event.h"
#include "ui/views/view.h"
namespace ash {
namespace calendar_metrics {
namespace {
constexpr char kCalendarViewShowSourcePrefix[] = "Ash.Calendar.ShowSource.";
constexpr char kCalendarDateCellActivated[] = "Ash.Calendar.DateCell.Activated";
constexpr char kCalendarEventListItemActivated[] =
"Ash.Calendar.EventListItem.Activated";
constexpr char kCalendarMonthDownArrowButtonActivated[] =
"Ash.Calendar.MonthDownArrowButton.Activated";
constexpr char kCalendarMonthUpArrowButtonActivated[] =
"Ash.Calendar.MonthUpArrowButton.Activated";
constexpr char kCalendarMonthDwellTime[] = "Ash.Calendar.MonthDwellTime";
constexpr char kCalendarScrollSource[] = "Ash.Calendar.ScrollSource";
constexpr char kCalendarKeyboardNavigation[] =
"Ash.Calendar.KeyboardNavigation";
constexpr char kCalendarEventListItemInUpNextPressed[] =
"Ash.Calendar.UpNextView.EventListItem.Pressed";
constexpr char kCalendarUpNextEventDisplayedCount[] =
"Ash.Calendar.UpNextView.EventDisplayedCount";
constexpr char kCalendarEventListItemJoinButtonPressed[] =
"Ash.Calendar.EventListView.JoinMeetingButton.Pressed";
constexpr char kCalendarUpNextJoinButtonPressed[] =
"Ash.Calendar.UpNextView.JoinMeetingButton.Pressed";
constexpr char kCalendarEventListEventDisplayedCount[] =
"Ash.Calendar.EventListViewJelly.EventDisplayedCount";
constexpr char kCalendarEventsDisplayedToUser[] =
"Ash.Calendar.EventsDisplayedToUser";
constexpr char kCalendarUserAction[] = "Ash.Calendar.UserAction";
constexpr char kCalendarWebUiOpened[] =
"Ash.Calendar.UserActionToOpenCalendarWebUi";
constexpr char kCalendarFetchCalendarsFetchDuration[] =
"Ash.Calendar.FetchCalendars.FetchDuration";
constexpr char kCalendarFetchCalendarsResult[] =
"Ash.Calendar.FetchCalendars.Result";
constexpr char kCalendarFetchCalendarsTimeout[] =
"Ash.Calendar.FetchCalendars.Timeout";
constexpr char kCalendarFetchEventsFetchDuration[] =
"Ash.Calendar.FetchEvents.FetchDuration";
constexpr char kCalendarFetchEventsResult[] = "Ash.Calendar.FetchEvents.Result";
constexpr char kCalendarFetchEventsSingleMonthSize[] =
"Ash.Calendar.FetchEvents.SingleMonthSize";
constexpr char kCalendarFetchEventsTimeout[] =
"Ash.Calendar.FetchEvents.Timeout";
constexpr char kCalendarFetchEventsTotalCacheSizeMonths[] =
"Ash.Calendar.FetchEvents.TotalCacheSizeMonths";
constexpr char kCalendarFetchEventsTotalFetchDuration[] =
"Ash.Calendar.FetchEvents.TotalFetchDuration";
constexpr char kCalendarFetchCalendarsTotalSelectedCalendars[] =
"Ash.Calendar.FetchCalendars.TotalSelectedCalendars";
constexpr char kCalendarTimeToSeeTodaysEventDots[] =
"Ash.Calendar.TimeToSeeTodaysEventDots";
constexpr char kCalendarTimeToSeeTodaysEventDotsForMultiCalendar[] =
"Ash.Calendar.TimeToSeeTodaysEventDotsForMultiCalendar";
// This enum is used in histograms. These values are persisted to logs. Entries
// should not be renumbered and numeric values should never be reused, only add
// at the end and. Also remember to update the CalendarUserAction enum in
// tools/metrics/histograms/metadata/ash/enums.xml.
enum class CalendarUserActionType {
kDateCellActivated = 0,
kMonthDownArrowActivated = 1,
kMonthUpArrowActivated = 2,
kScrolled = 3,
kKeyboardNavigation = 4,
kEventListItemPressed = 5,
kUpNextItemPressed = 6,
kEventListItemJoinButtonPressed = 7,
kUpNextJoinButtonPressed = 8,
kResetToTodayPressed = 9,
kTodaysEventsInUpNextPressed = 10,
kScrollInUpNext = 11,
kCalendarLaunchedFromEmptyEventList = 12,
kEventListClosed = 13,
kSettingsButtonPressed = 14,
kMaxValue = kSettingsButtonPressed,
};
bool ActionOpensCalendarWebUi(CalendarUserActionType action) {
switch (action) {
case CalendarUserActionType::kDateCellActivated:
case CalendarUserActionType::kMonthDownArrowActivated:
case CalendarUserActionType::kMonthUpArrowActivated:
case CalendarUserActionType::kScrolled:
case CalendarUserActionType::kKeyboardNavigation:
case CalendarUserActionType::kResetToTodayPressed:
case CalendarUserActionType::kTodaysEventsInUpNextPressed:
case CalendarUserActionType::kScrollInUpNext:
case CalendarUserActionType::kEventListClosed:
case CalendarUserActionType::kSettingsButtonPressed:
return false;
case CalendarUserActionType::kEventListItemPressed:
case CalendarUserActionType::kUpNextItemPressed:
case CalendarUserActionType::kEventListItemJoinButtonPressed:
case CalendarUserActionType::kUpNextJoinButtonPressed:
case CalendarUserActionType::kCalendarLaunchedFromEmptyEventList:
return true;
}
}
void RecordCalendarUserAction(CalendarUserActionType action) {
if (ActionOpensCalendarWebUi(action)) {
base::UmaHistogramEnumeration(kCalendarWebUiOpened, action);
}
base::UmaHistogramEnumeration(kCalendarUserAction, action);
}
} // namespace
CalendarEventSource GetEventType(const ui::Event& event) {
if (event.IsGestureEvent())
return CalendarEventSource::kTap;
if (event.IsMouseEvent())
return CalendarEventSource::kClick;
if (event.IsKeyEvent())
return CalendarEventSource::kKeyboard;
if (event.IsTouchEvent())
return CalendarEventSource::kStylus;
NOTREACHED();
}
void RecordCalendarShowMetrics(
CalendarViewShowSource show_source,
calendar_metrics::CalendarEventSource event_source) {
std::string histogram_name = kCalendarViewShowSourcePrefix;
switch (show_source) {
case CalendarViewShowSource::kDateView:
histogram_name += "DateView";
break;
case CalendarViewShowSource::kTimeView:
histogram_name += "TimeView";
break;
case CalendarViewShowSource::kAccelerator:
histogram_name += "Keyboard";
break;
}
base::UmaHistogramEnumeration(histogram_name, event_source);
}
void RecordCalendarDateCellActivated(const ui::Event& event) {
RecordCalendarUserAction(CalendarUserActionType::kDateCellActivated);
base::UmaHistogramEnumeration(kCalendarDateCellActivated,
GetEventType(event));
}
void RecordMonthArrowButtonActivated(bool up, const ui::Event& event) {
RecordCalendarUserAction(
up ? CalendarUserActionType::kMonthUpArrowActivated
: CalendarUserActionType::kMonthDownArrowActivated);
base::UmaHistogramEnumeration(up ? kCalendarMonthUpArrowButtonActivated
: kCalendarMonthDownArrowButtonActivated,
GetEventType(event));
}
void RecordEventListItemActivated(const ui::Event& event) {
RecordCalendarUserAction(CalendarUserActionType::kEventListItemPressed);
base::UmaHistogramEnumeration(kCalendarEventListItemActivated,
GetEventType(event));
}
void RecordEventListForTodayActivated() {
RecordCalendarUserAction(
CalendarUserActionType::kTodaysEventsInUpNextPressed);
}
void RecordMonthDwellTime(const base::TimeDelta& dwell_time) {
base::UmaHistogramMediumTimes(kCalendarMonthDwellTime, dwell_time);
}
void RecordResetToTodayPressed() {
RecordCalendarUserAction(CalendarUserActionType::kResetToTodayPressed);
}
void RecordScrollSource(CalendarViewScrollSource source) {
RecordCalendarUserAction(CalendarUserActionType::kScrolled);
base::UmaHistogramEnumeration(kCalendarScrollSource, source);
}
ui::AnimationThroughputReporter CreateAnimationReporter(
views::View* view,
const std::string& animation_histogram_name) {
// TODO(crbug.com/1297376): Add unit tests for animation metrics recording.
return ui::AnimationThroughputReporter(
view->layer()->GetAnimator(),
metrics_util::ForSmoothnessV3(base::BindRepeating(
[](const std::string& animation_histogram_name, int smoothness) {
base::UmaHistogramPercentage(animation_histogram_name, smoothness);
},
animation_histogram_name)));
}
void RecordCalendarKeyboardNavigation(
const CalendarKeyboardNavigationSource key_source) {
RecordCalendarUserAction(CalendarUserActionType::kKeyboardNavigation);
base::UmaHistogramEnumeration(kCalendarKeyboardNavigation, key_source);
}
void RecordEventListItemInUpNextLaunched(const ui::Event& event) {
RecordCalendarUserAction(CalendarUserActionType::kUpNextItemPressed);
base::UmaHistogramEnumeration(kCalendarEventListItemInUpNextPressed,
GetEventType(event));
}
void RecordUpNextEventCount(const int event_count) {
base::UmaHistogramCounts100(kCalendarUpNextEventDisplayedCount, event_count);
}
void RecordJoinButtonPressedFromEventListView(const ui::Event& event) {
RecordCalendarUserAction(
CalendarUserActionType::kEventListItemJoinButtonPressed);
base::UmaHistogramEnumeration(kCalendarEventListItemJoinButtonPressed,
GetEventType(event));
}
void RecordJoinButtonPressedFromUpNextView(const ui::Event& event) {
RecordCalendarUserAction(CalendarUserActionType::kUpNextJoinButtonPressed);
base::UmaHistogramEnumeration(kCalendarUpNextJoinButtonPressed,
GetEventType(event));
}
void RecordEventListEventCount(const int event_count) {
base::UmaHistogramCounts100(kCalendarEventListEventDisplayedCount,
event_count);
}
void RecordEventsDisplayedToUser() {
base::UmaHistogramBoolean(kCalendarEventsDisplayedToUser, true);
}
void RecordScrollEventInUpNext() {
RecordCalendarUserAction(CalendarUserActionType::kScrollInUpNext);
}
void RecordCalendarLaunchedFromEmptyEventList() {
RecordCalendarUserAction(
CalendarUserActionType::kCalendarLaunchedFromEmptyEventList);
}
void RecordEventListClosed() {
RecordCalendarUserAction(CalendarUserActionType::kEventListClosed);
}
void RecordSettingsButtonPressed() {
RecordCalendarUserAction(CalendarUserActionType::kSettingsButtonPressed);
}
void RecordCalendarListFetchDuration(const base::TimeDelta fetch_duration) {
base::UmaHistogramTimes(kCalendarFetchCalendarsFetchDuration, fetch_duration);
}
void RecordCalendarListFetchErrorCode(google_apis::ApiErrorCode error) {
base::UmaHistogramSparse(kCalendarFetchCalendarsResult, error);
}
void RecordCalendarListFetchTimeout(bool fetch_timed_out) {
base::UmaHistogramBoolean(kCalendarFetchCalendarsTimeout, fetch_timed_out);
}
void RecordEventListFetchDuration(const base::TimeDelta fetch_duration) {
base::UmaHistogramTimes(kCalendarFetchEventsFetchDuration, fetch_duration);
}
void RecordEventListFetchErrorCode(google_apis::ApiErrorCode error) {
base::UmaHistogramSparse(kCalendarFetchEventsResult, error);
}
void RecordEventListFetchTimeout(bool fetch_timed_out) {
base::UmaHistogramBoolean(kCalendarFetchEventsTimeout, fetch_timed_out);
}
void RecordEventListFetchesTotalDuration(const base::TimeDelta fetch_duration) {
base::UmaHistogramTimes(kCalendarFetchEventsTotalFetchDuration,
fetch_duration);
}
void RecordSingleMonthSizeInBytes(size_t single_month_cache_size) {
base::UmaHistogramCounts1M(kCalendarFetchEventsSingleMonthSize,
single_month_cache_size);
}
void RecordTotalEventsCacheSizeInMonths(unsigned int events_cache_size) {
base::UmaHistogramCounts100000(kCalendarFetchEventsTotalCacheSizeMonths,
events_cache_size);
}
void RecordTotalSelectedCalendars(unsigned int selected_calendars) {
base::UmaHistogramCounts100000(kCalendarFetchCalendarsTotalSelectedCalendars,
selected_calendars);
}
void RecordTimeToSeeTodaysEventDots(const base::TimeDelta time_elapsed,
bool multi_calendar_enabled) {
if (multi_calendar_enabled) {
base::UmaHistogramMediumTimes(
kCalendarTimeToSeeTodaysEventDotsForMultiCalendar, time_elapsed);
} else {
base::UmaHistogramMediumTimes(kCalendarTimeToSeeTodaysEventDots,
time_elapsed);
}
}
} // namespace calendar_metrics
} // namespace ash