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
|
// 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 "components/feed/core/v2/stream/info_card_tracker.h"
#include <algorithm>
#include "base/base64.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/feed/core/common/pref_names.h"
#include "components/feed/core/v2/config.h"
#include "components/feed/core/v2/feedstore_util.h"
#include "components/feed/core/v2/proto_util.h"
#include "components/feed/feed_feature_list.h"
#include "components/prefs/pref_service.h"
namespace feed {
using feedwire::InfoCardTrackingState;
namespace {
std::string InfoCardTypeToString(int info_card_type) {
return base::NumberToString(info_card_type);
}
bool compareInfoCardTrackingState(const InfoCardTrackingState& i1,
const InfoCardTrackingState& i2) {
return (i1.type() < i2.type());
}
InfoCardTrackingState DecodeFromBase64SerializedString(
const std::string& base64_serialized_state) {
InfoCardTrackingState state;
std::string serialized_state;
if (!base::Base64Decode(base64_serialized_state, &serialized_state)) {
DLOG(ERROR) << "Error decoding persisted state from base64";
return state;
}
if (!state.ParseFromString(serialized_state))
DLOG(ERROR) << "Error parsing InfoCardTrackingState message";
return state;
}
int64_t GetAdjustedViewTimestamp(int64_t view_timestamp,
int64_t server_timestamp,
int64_t timestamp_adjustment) {
view_timestamp += timestamp_adjustment;
// Ensure that the view timestamp does not get earlier than the server.
if (view_timestamp < server_timestamp)
view_timestamp = server_timestamp;
// Ensure that the view timestamp does not exceed the lifetime of the content.
int64_t max_timestamp =
server_timestamp +
GetFeedConfig().content_expiration_threshold.InMilliseconds();
if (view_timestamp > max_timestamp)
view_timestamp = max_timestamp;
return view_timestamp;
}
} // namespace
InfoCardTracker::InfoCardTracker(PrefService* profile_prefs)
: profile_prefs_(profile_prefs) {
DCHECK(profile_prefs_);
}
InfoCardTracker::~InfoCardTracker() = default;
std::vector<InfoCardTrackingState> InfoCardTracker::GetAllStates(
int64_t server_timestamp,
int64_t client_timestamp) const {
std::vector<InfoCardTrackingState> states;
const base::Value& dict = profile_prefs_->GetValue(prefs::kInfoCardStates);
if (dict.is_dict()) {
int64_t timestamp_adjustment = server_timestamp - client_timestamp;
for (const auto pair : dict.GetDict()) {
int info_card_type = 0;
if (!base::StringToInt(pair.first, &info_card_type))
continue;
if (!pair.second.is_string())
continue;
InfoCardTrackingState state =
DecodeFromBase64SerializedString(pair.second.GetString());
state.set_type(info_card_type);
if (state.has_first_view_timestamp()) {
state.set_first_view_timestamp(
GetAdjustedViewTimestamp(state.first_view_timestamp(),
server_timestamp, timestamp_adjustment));
}
if (state.has_last_view_timestamp()) {
state.set_last_view_timestamp(
GetAdjustedViewTimestamp(state.last_view_timestamp(),
server_timestamp, timestamp_adjustment));
}
states.push_back(state);
}
}
std::sort(states.begin(), states.end(), compareInfoCardTrackingState);
return states;
}
void InfoCardTracker::OnViewed(int info_card_type,
int minimum_view_interval_seconds) {
auto now = base::Time::Now();
InfoCardTrackingState state = GetState(info_card_type);
if (state.has_last_view_timestamp() &&
now - feedstore::FromTimestampMillis(state.last_view_timestamp()) <
base::Seconds(minimum_view_interval_seconds)) {
return;
}
state.set_view_count(state.view_count() + 1);
if (!state.has_first_view_timestamp())
state.set_first_view_timestamp(feedstore::ToTimestampMillis(now));
state.set_last_view_timestamp(feedstore::ToTimestampMillis(now));
SetState(info_card_type, state);
}
void InfoCardTracker::OnClicked(int info_card_type) {
InfoCardTrackingState state = GetState(info_card_type);
state.set_click_count(state.click_count() + 1);
SetState(info_card_type, state);
}
void InfoCardTracker::OnDismissed(int info_card_type) {
InfoCardTrackingState state = GetState(info_card_type);
state.set_explicitly_dismissed_count(state.explicitly_dismissed_count() + 1);
SetState(info_card_type, state);
}
void InfoCardTracker::ResetState(int info_card_type) {
InfoCardTrackingState state;
SetState(info_card_type, state);
}
InfoCardTrackingState InfoCardTracker::GetState(int info_card_type) const {
const base::Value::Dict& all_states =
profile_prefs_->GetDict(prefs::kInfoCardStates);
const std::string* base64_serialized_state =
all_states.FindString(InfoCardTypeToString(info_card_type));
if (!base64_serialized_state)
return InfoCardTrackingState();
return DecodeFromBase64SerializedString(*base64_serialized_state);
}
void InfoCardTracker::SetState(int info_card_type,
const InfoCardTrackingState& state) {
// SerializeToString encodes the proto into a series of bytes that is not
// going to be compatible with UTF-8 encoding. We need to convert them to
// base64 before writing to the prefs store.
std::string serialized_state;
state.SerializeToString(&serialized_state);
const base::Value::Dict& states =
profile_prefs_->GetDict(prefs::kInfoCardStates);
base::Value::Dict updated_states = states.Clone();
updated_states.Set(InfoCardTypeToString(info_card_type),
base::Base64Encode(serialized_state));
profile_prefs_->SetDict(prefs::kInfoCardStates, std::move(updated_states));
}
} // namespace feed
|