blob: aad4daa7674bbfac6eba375817ab2362bfe5f981 [file] [log] [blame]
// Copyright (c) 2016 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 "ios/chrome/browser/notification_promo.h"
#include <stddef.h>
#include <utility>
#include "base/check.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/variations/variations_associated_data.h"
namespace ios {
const char kNTPPromoFinchExperiment[] = "IOSDefaultBrowerNTPPromotion";
namespace {
// The name of the preference that stores the promotion object.
const char kPrefPromoObject[] = "ios.ntppromo";
// Keys in the kPrefPromoObject dictionary; used only here.
const char kPrefPromoFirstViewTime[] = "first_view_time";
const char kPrefPromoViews[] = "views";
const char kPrefPromoClosed[] = "closed";
} // namespace
NotificationPromo::NotificationPromo(PrefService* local_state)
: local_state_(local_state),
promo_payload_(base::Value::Type::DICTIONARY),
start_(0.0),
end_(0.0),
promo_id_(-1),
max_views_(0),
max_seconds_(0),
first_view_time_(0),
views_(0),
closed_(false) {
DCHECK(local_state_);
}
NotificationPromo::~NotificationPromo() {}
void NotificationPromo::InitFromVariations() {
std::map<std::string, std::string> params;
if (!variations::GetVariationParams(kNTPPromoFinchExperiment, &params)) {
return;
}
// Build dictionary of parameters to pass to |InitFromJson|. Some parameters
// are stored in a payload subdictionary, so two dictionaries are
// built: one to represent the payload and one to represent all of the
// paremeters. The payload is then added to the overall dictionary. This code
// can be removed if this class is refactored and the payload is then
// disregarded (crbug.com/608525).
base::Value json(base::Value::Type::DICTIONARY);
base::Value payload(base::Value::Type::DICTIONARY);
for (const auto& param : params) {
int converted_number;
bool converted = base::StringToInt(param.second, &converted_number);
// Choose the dictionary to which parameter is added based on whether the
// parameter belongs in the payload or not.
base::Value& json_or_payload = IsPayloadParam(param.first) ? payload : json;
if (converted) {
json_or_payload.SetKey(param.first, base::Value(converted_number));
} else {
json_or_payload.SetKey(param.first, base::Value(param.second));
}
}
json.SetKey("payload", std::move(payload));
InitFromJson(std::move(json));
}
void NotificationPromo::InitFromJson(base::Value promo) {
base::Time time;
const std::string* str = nullptr;
str = promo.FindStringKey("start");
if (str && base::Time::FromString(str->c_str(), &time)) {
start_ = time.ToDoubleT();
DVLOG(1) << "start str=" << *str
<< ", start_=" << base::NumberToString(start_);
}
str = promo.FindStringKey("end");
if (str && base::Time::FromString(str->c_str(), &time)) {
end_ = time.ToDoubleT();
DVLOG(1) << "end str =" << *str << ", end_=" << base::NumberToString(end_);
}
str = promo.FindStringKey("promo_text");
if (str)
promo_text_ = *str;
DVLOG(1) << "promo_text_=" << promo_text_;
const base::Value* payload =
promo.FindKeyOfType("payload", base::Value::Type::DICTIONARY);
if (payload) {
promo_payload_ = payload->Clone();
}
base::Optional<int> max_views = promo.FindIntKey("max_views");
if (max_views.has_value())
max_views_ = max_views.value();
DVLOG(1) << "max_views_ " << max_views_;
base::Optional<int> max_seconds = promo.FindIntKey("max_seconds");
if (max_seconds.has_value())
max_seconds_ = max_seconds.value();
DVLOG(1) << "max_seconds_ " << max_seconds_;
base::Optional<int> promo_id = promo.FindIntKey("promo_id");
if (promo_id.has_value())
promo_id_ = promo_id.value();
DVLOG(1) << "promo_id_ " << promo_id_;
}
// static
void NotificationPromo::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kPrefPromoObject);
}
// static
void NotificationPromo::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterDictionaryPref(kPrefPromoObject);
}
// static
void NotificationPromo::MigrateUserPrefs(PrefService* user_prefs) {
user_prefs->ClearPref(kPrefPromoObject);
}
void NotificationPromo::WritePrefs() {
WritePrefs(promo_id_, first_view_time_, views_, closed_);
}
void NotificationPromo::WritePrefs(int promo_id,
double first_view_time,
int views,
bool closed) {
auto ntp_promo = std::make_unique<base::DictionaryValue>();
ntp_promo->SetDouble(kPrefPromoFirstViewTime, first_view_time);
ntp_promo->SetInteger(kPrefPromoViews, views);
ntp_promo->SetBoolean(kPrefPromoClosed, closed);
base::DictionaryValue promo_dict;
promo_dict.MergeDictionary(local_state_->GetDictionary(kPrefPromoObject));
promo_dict.Set(base::NumberToString(promo_id), std::move(ntp_promo));
local_state_->Set(kPrefPromoObject, promo_dict);
DVLOG(1) << "WritePrefs " << promo_dict;
}
void NotificationPromo::InitFromPrefs() {
// If |promo_id_| is not set, do nothing.
if (promo_id_ == -1)
return;
const base::DictionaryValue* promo_dict =
local_state_->GetDictionary(kPrefPromoObject);
if (!promo_dict)
return;
const base::DictionaryValue* ntp_promo = NULL;
promo_dict->GetDictionary(base::NumberToString(promo_id_), &ntp_promo);
if (!ntp_promo)
return;
ntp_promo->GetDouble(kPrefPromoFirstViewTime, &first_view_time_);
ntp_promo->GetInteger(kPrefPromoViews, &views_);
ntp_promo->GetBoolean(kPrefPromoClosed, &closed_);
}
bool NotificationPromo::CanShow() const {
return !closed_ && !promo_text_.empty() && !ExceedsMaxViews() &&
!ExceedsMaxSeconds() &&
base::Time::FromDoubleT(StartTime()) < base::Time::Now() &&
base::Time::FromDoubleT(EndTime()) > base::Time::Now();
}
void NotificationPromo::HandleClosed() {
if (!closed_) {
WritePrefs(promo_id_, first_view_time_, views_, true);
}
}
void NotificationPromo::HandleViewed() {
int views = views_ + 1;
double first_view_time = first_view_time_;
if (first_view_time == 0) {
first_view_time = base::Time::Now().ToDoubleT();
}
WritePrefs(promo_id_, first_view_time, views, closed_);
}
bool NotificationPromo::ExceedsMaxViews() const {
return (max_views_ == 0) ? false : views_ >= max_views_;
}
bool NotificationPromo::ExceedsMaxSeconds() const {
if (max_seconds_ == 0 || first_view_time_ == 0)
return false;
const base::Time last_view_time = base::Time::FromDoubleT(first_view_time_) +
base::TimeDelta::FromSeconds(max_seconds_);
return last_view_time < base::Time::Now();
}
bool NotificationPromo::IsPayloadParam(const std::string& param_name) const {
return param_name != "start" && param_name != "end" &&
param_name != "promo_text" && param_name != "max_views" &&
param_name != "max_seconds" && param_name != "promo_id";
}
double NotificationPromo::StartTime() const {
return start_;
}
double NotificationPromo::EndTime() const {
return end_;
}
} // namespace ios