blob: c15b0d812cced337e8e65d4f7023516d59b7709c [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/metrics/stylus_metrics_recorder.h"
#include "ash/shell.h"
#include "base/logging.h"
/* Emit metrics related to stylus utilization:
* StylusDetachedFromGarageSession
* StylusDetachedFromDockSession
* StylusDetachedFromGarageOrDockSession
* TODO(kenalba):
* + Usage of stylus (interacting with screen) while stylus is docked (only
* once a day).
* + Usage of stylus (interacting with screen) while stylus is attached (only
* once a day).
* + Usage of stylus (interacting with screen) while stylus is undocked (only
* once a day).
* + Usage of stylus (interacting with screen) while stylus is unattached
* (only once a day).
* + Usage of stylus (interacting with screen) while stylus is
* undocked/unattached (only once a day).
*
* Not currently possible with SFUL:
* Length of 'failed' vs. 'successful', aka a session
* where the pen was used vs. one where it was not.
* This is not possible as failed sessions don't have
* usetime, and the failure/success of a session
* needs to be known at the beginning of the session.
*
* Math between different metrics, hence several combinations
* need to be emitted in multiple metrics.
*/
namespace ash {
namespace {
bool IsStylusOnCharge(const PeripheralBatteryListener::BatteryInfo& battery) {
return (
battery.charge_status !=
PeripheralBatteryListener::BatteryInfo::ChargeStatus::kUnknown &&
battery.charge_status !=
PeripheralBatteryListener::BatteryInfo::ChargeStatus::kDischarging);
}
} // namespace
StylusSessionMetricsDelegate::StylusSessionMetricsDelegate(
const std::string& feature_name)
: metrics_(feature_name, this) {}
StylusSessionMetricsDelegate::~StylusSessionMetricsDelegate() = default;
bool StylusSessionMetricsDelegate::IsEligible() const {
return capable_;
}
bool StylusSessionMetricsDelegate::IsEnabled() const {
return capable_;
}
void StylusSessionMetricsDelegate::SetState(bool now_capable, bool in_session) {
if (active_ && (!in_session || !now_capable)) {
metrics_.StopSuccessfulUsage();
active_ = false;
}
capable_ = now_capable;
if (!active_ && in_session && now_capable) {
metrics_.RecordUsage(true);
metrics_.StartSuccessfulUsage();
active_ = true;
}
}
StylusMetricsRecorder::StylusMetricsRecorder() {
UpdateStylusState();
DCHECK(Shell::HasInstance());
DCHECK(Shell::Get()->peripheral_battery_listener());
Shell::Get()->peripheral_battery_listener()->AddObserver(this);
}
StylusMetricsRecorder::~StylusMetricsRecorder() {
Shell::Get()->peripheral_battery_listener()->RemoveObserver(this);
}
void StylusMetricsRecorder::OnAddingBattery(
const PeripheralBatteryListener::BatteryInfo& battery) {
if (battery.type == PeripheralBatteryListener::BatteryInfo::PeripheralType::
kStylusViaCharger) {
// Record the presence of the specific charger type; the API does
// not imply they are exclusive.
// TODO(kenalba): Avoid hard-coding this key
if (battery.key == "garaged-stylus-charger")
stylus_garage_present_ = true;
else
stylus_dock_present_ = true;
UpdateStylusState();
}
}
void StylusMetricsRecorder::OnRemovingBattery(
const PeripheralBatteryListener::BatteryInfo& battery) {
if (battery.type == PeripheralBatteryListener::BatteryInfo::PeripheralType::
kStylusViaCharger) {
// TODO(kenalba): Avoid hard-coding this key
if (battery.key == "garaged-stylus-charger")
stylus_garage_present_ = false;
else
stylus_dock_present_ = false;
stylus_on_charge_.reset();
UpdateStylusState();
}
}
void StylusMetricsRecorder::OnUpdatedBatteryLevel(
const PeripheralBatteryListener::BatteryInfo& battery) {
if (battery.type == PeripheralBatteryListener::BatteryInfo::PeripheralType::
kStylusViaCharger) {
stylus_on_charge_ = IsStylusOnCharge(battery);
UpdateStylusState();
}
}
void StylusMetricsRecorder::UpdateStylusState() {
/* Sessions are recorded when we know the device is capable of
* having a stylus garaged or docked, and the stylus is not on charge,
* and therefore not currently garaged or docked.
*/
const bool stylus_off_charge =
stylus_on_charge_.has_value() && *stylus_on_charge_ == false;
const bool stylus_detached_from_garage =
stylus_garage_present_ && stylus_off_charge;
const bool stylus_detached_from_dock =
stylus_dock_present_ && stylus_off_charge;
stylus_detached_from_garage_session_metrics_delegate_.SetState(
stylus_garage_present_, stylus_detached_from_garage);
stylus_detached_from_dock_session_metrics_delegate_.SetState(
stylus_dock_present_, stylus_detached_from_dock);
stylus_detached_from_garage_or_dock_session_metrics_delegate_.SetState(
stylus_garage_present_ || stylus_dock_present_,
stylus_detached_from_garage || stylus_detached_from_dock);
}
} // namespace ash