1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
// 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 "chrome/browser/ash/input_method/pref_change_recorder.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/values.h"
#include "chrome/browser/ash/input_method/autocorrect_prefs.h"
#include "chrome/common/pref_names.h"
namespace ash::input_method {
namespace {
constexpr char kUsEnglish[] = "xkb:us::eng";
using AutocorrectPrefDetails = PrefChangeRecorder::AutocorrectPrefDetails;
using AutocorrectPrefs = PrefChangeRecorder::AutocorrectPrefs;
using KeyboardType = PrefChangeRecorder::KeyboardType;
struct AutocorrectPrefChange {
AutocorrectPrefDetails details;
AutocorrectPreference previous_value;
AutocorrectPreference new_value;
};
// Extracts all autocorrect preference values from the input method options
// dictionary blob. Each preference will be keyed to a single engine id, with
// exactly two preferences per engine id for VK and PK.
AutocorrectPrefs ExtractAutocorrectPrefs(PrefService* pref_service) {
AutocorrectPrefs autocorrect_prefs;
for (const auto [engine_id, _] :
pref_service->GetDict(prefs::kLanguageInputMethodSpecificSettings)) {
autocorrect_prefs.insert(
{base::StrCat({engine_id, ".VirtualKeyboard"}),
AutocorrectPrefDetails{
/*engine_id=*/engine_id,
/*keyboard_type=*/KeyboardType::kVirtualKeyboard,
/*preference=*/
GetVirtualKeyboardAutocorrectPref(*(pref_service), engine_id)}});
autocorrect_prefs.insert(
{base::StrCat({engine_id, ".PhysicalKeyboard"}),
AutocorrectPrefDetails{
/*engine_id=*/engine_id,
/*keyboard_type=*/KeyboardType::kPhysicalKeyboard,
/*preference=*/
GetPhysicalKeyboardAutocorrectPref(*(pref_service), engine_id)}});
}
return autocorrect_prefs;
}
// Iterate over two sets of autocorrect preferences and find the first
// preference where its respective value differs between the two sets. This
// function will be called once per setting change, so we can assume there
// would be at most one preference with different values in the two sets.
std::optional<AutocorrectPrefChange> FindPrefChange(
const AutocorrectPrefs& previous,
const AutocorrectPrefs& current) {
for (const auto& [key, details] : current) {
const auto find_it = previous.find(key);
if (find_it == previous.end() &&
details.preference != AutocorrectPreference::kDefault) {
return AutocorrectPrefChange{
/*details=*/details,
/*previous_value=*/AutocorrectPreference::kDefault,
/*new_value=*/details.preference};
}
if (find_it != previous.end()) {
const AutocorrectPrefDetails& previous_details = find_it->second;
if (previous_details.preference != details.preference) {
return AutocorrectPrefChange{
/*details=*/details,
/*previous_value=*/previous_details.preference,
/*new_value=*/details.preference};
}
}
}
return std::nullopt;
}
AutocorrectPrefStateTransition MapToAutocorrectPrefStateTransition(
AutocorrectPreference previous_value,
AutocorrectPreference new_value) {
if (previous_value == AutocorrectPreference::kDefault &&
new_value == AutocorrectPreference::kDisabled) {
return AutocorrectPrefStateTransition::kDefaultToDisabled;
}
if (previous_value == AutocorrectPreference::kDefault &&
new_value == AutocorrectPreference::kEnabled) {
return AutocorrectPrefStateTransition::kDefaultToEnabled;
}
if (previous_value == AutocorrectPreference::kDisabled &&
new_value == AutocorrectPreference::kEnabled) {
return AutocorrectPrefStateTransition::kDisabledToEnabled;
}
if (previous_value == AutocorrectPreference::kEnabled &&
new_value == AutocorrectPreference::kDisabled) {
return AutocorrectPrefStateTransition::kEnabledToDisabled;
}
if (previous_value == AutocorrectPreference::kDefault &&
new_value == AutocorrectPreference::kEnabledByDefault) {
return AutocorrectPrefStateTransition::kDefaultToForceEnabled;
}
if (previous_value == AutocorrectPreference::kEnabledByDefault &&
new_value == AutocorrectPreference::kDisabled) {
return AutocorrectPrefStateTransition::kForceEnabledToDisabled;
}
if (previous_value == AutocorrectPreference::kEnabledByDefault &&
new_value == AutocorrectPreference::kEnabled) {
return AutocorrectPrefStateTransition::kForceEnabledToEnabled;
}
// Note that we do not record kEnabledByDefault to kDefault (this transition
// would occur in a rampdown of the enabled by default experiment). Recording
// this transition would require code to run outside of the enabled by default
// flag (ie remove the enabledByDefault bool from prefs when the experiment
// flag is disabled), so on the safe side we don't do that.
return AutocorrectPrefStateTransition::kUnchanged;
}
void RecordAutocorrectPrefChangeMetric(
const AutocorrectPrefChange& pref_change) {
AutocorrectPrefStateTransition state_transition =
MapToAutocorrectPrefStateTransition(pref_change.previous_value,
pref_change.new_value);
if (pref_change.details.engine_id == kUsEnglish) {
base::UmaHistogramEnumeration(
pref_change.details.keyboard_type == KeyboardType::kPhysicalKeyboard
? "InputMethod.Assistive.AutocorrectV2.UserPrefChange.English.PK"
: "InputMethod.Assistive.AutocorrectV2.UserPrefChange.English.VK",
state_transition);
}
base::UmaHistogramEnumeration(
pref_change.details.keyboard_type == KeyboardType::kPhysicalKeyboard
? "InputMethod.Assistive.AutocorrectV2.UserPrefChange.All.PK"
: "InputMethod.Assistive.AutocorrectV2.UserPrefChange.All.VK",
state_transition);
}
} // namespace
PrefChangeRecorder::PrefChangeRecorder(PrefService* pref_service)
: input_method_options_observer_(pref_service),
autocorrect_prefs_(ExtractAutocorrectPrefs(pref_service)),
pref_service_(pref_service) {
input_method_options_observer_.Observe(
base::BindRepeating(&PrefChangeRecorder::OnInputMethodOptionsChanged,
weak_ptr_factory_.GetWeakPtr()));
}
PrefChangeRecorder::~PrefChangeRecorder() = default;
void PrefChangeRecorder::OnInputMethodOptionsChanged(
const std::string& pref_path_changed) {
AutocorrectPrefs new_autocorrect_prefs =
ExtractAutocorrectPrefs(pref_service_);
auto pref_change = FindPrefChange(autocorrect_prefs_, new_autocorrect_prefs);
if (pref_change) {
RecordAutocorrectPrefChangeMetric(*pref_change);
}
autocorrect_prefs_ = std::move(new_autocorrect_prefs);
}
} // namespace ash::input_method
|