Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/web_resource/promo_notification.h" | |
| 6 | |
| 7 #include "base/rand_util.h" | |
| 8 #include "base/string_number_conversions.h" | |
| 9 #include "base/time.h" | |
| 10 #include "base/values.h" | |
| 11 #include "chrome/browser/prefs/pref_service.h" | |
| 12 #include "chrome/browser/web_resource/promo_resource_service.h" | |
| 13 #include "chrome/common/pref_names.h" | |
| 14 | |
| 15 // Maximum number of hours for each time slice (4 weeks). | |
| 16 static const int kMaxTimeSliceHours = 24 * 7 * 4; | |
| 17 | |
| 18 PromoNotification::PromoNotification(PrefService* prefs, Delegate* delegate) | |
| 19 : prefs_(prefs), | |
| 20 delegate_(delegate), | |
| 21 start_(0.0), | |
| 22 end_(0.0), | |
| 23 build_(0), | |
| 24 time_slice_(0), | |
| 25 max_group_(0), | |
| 26 max_views_(0), | |
| 27 group_(0), | |
| 28 views_(0), | |
| 29 text_(), | |
| 30 closed_(false) { | |
| 31 DCHECK(prefs); | |
| 32 } | |
| 33 | |
| 34 void PromoNotification::InitFromJson(const DictionaryValue& json) { | |
| 35 DictionaryValue* dict; | |
| 36 if (json.GetDictionary("topic", &dict)) { | |
| 37 ListValue* answers; | |
| 38 if (dict->GetList("answers", &answers)) { | |
| 39 for (ListValue::const_iterator it = answers->begin(); | |
| 40 it != answers->end(); | |
| 41 ++it) { | |
| 42 if ((*it)->IsType(Value::TYPE_DICTIONARY)) | |
| 43 Parse(static_cast<DictionaryValue*>(*it)); | |
| 44 } | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 CheckForNewNotification(); | |
| 49 } | |
| 50 | |
| 51 void PromoNotification::Parse(const DictionaryValue* dict) { | |
| 52 std::string name; | |
| 53 if (dict->GetString("name", &name)) { | |
| 54 if (name == "promo_start") { | |
| 55 GetParams(dict); | |
| 56 dict->GetString("tooltip", &text_); | |
| 57 start_ = GetTimeFromDict(dict); | |
| 58 } else if (name == "promo_end") { | |
| 59 end_ = GetTimeFromDict(dict); | |
| 60 } | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 bool PromoNotification::OutOfBounds(int var, int min, int max) { | |
| 65 return var < min || var > max; | |
| 66 } | |
| 67 | |
| 68 void PromoNotification::GetParams(const DictionaryValue* dict) { | |
| 69 std::string question; | |
| 70 dict->GetString("question", &question); | |
| 71 | |
| 72 size_t index = 0; | |
| 73 bool err = false; | |
| 74 | |
| 75 build_ = GetNextQuestionValue(question, &index, &err); | |
| 76 time_slice_ = GetNextQuestionValue(question, &index, &err); | |
| 77 max_group_ = GetNextQuestionValue(question, &index, &err); | |
| 78 max_views_ = GetNextQuestionValue(question, &index, &err); | |
| 79 | |
| 80 if (err || | |
| 81 OutOfBounds(build_, PromoResourceService::NO_BUILD, | |
| 82 PromoResourceService::ALL_BUILDS) || | |
| 83 OutOfBounds(time_slice_, 0, kMaxTimeSliceHours) || | |
| 84 OutOfBounds(max_group_, 0, kMaxGroupSize) || | |
| 85 OutOfBounds(max_views_, 0, kMaxViews)) { | |
| 86 // If values are not valid, do not show promo notification. | |
| 87 NOTREACHED() << "Invalid server data, question=" << question << | |
| 88 ", build=" << build_ << | |
| 89 ", time_slice=" << time_slice_ << | |
| 90 ", max_group=" << max_group_ << | |
| 91 ", max_views=" << max_views_; | |
| 92 build_ = PromoResourceService::NO_BUILD; | |
| 93 time_slice_ = 0; | |
| 94 max_group_ = 0; | |
| 95 max_views_ = 0; | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 void PromoNotification::CheckForNewNotification() { | |
| 100 const double old_start = GetTimeFromPrefs(prefs::kNTPPromoStart); | |
| 101 const double old_end = GetTimeFromPrefs(prefs::kNTPPromoEnd); | |
| 102 const bool has_views = prefs_->HasPrefPath(prefs::kNTPPromoViewsMax); | |
| 103 | |
| 104 // Trigger a new notification if the times have changed, or if | |
| 105 // we previously never wrote out a max_views preference. | |
| 106 if (old_start != start_ || old_end != end_ || !has_views) | |
| 107 OnNewNotification(); | |
| 108 } | |
| 109 | |
| 110 void PromoNotification::OnNewNotification() { | |
| 111 group_ = NewGroup(); | |
| 112 WritePrefs(); | |
| 113 if (delegate_) | |
| 114 delegate_->OnNewNotification(StartTimeWithOffset(), end_); | |
| 115 } | |
| 116 | |
| 117 // static | |
| 118 int PromoNotification::NewGroup() { | |
| 119 return base::RandInt(0, kMaxGroupSize); | |
| 120 } | |
| 121 | |
| 122 // static | |
| 123 void PromoNotification::RegisterUserPrefs(PrefService* prefs) { | |
|
Dan Beam
2011/09/27 03:27:55
I still think that all of the below would be best
achuithb
2011/09/27 08:03:19
I agree. We should probably have the promo class r
| |
| 124 prefs->RegisterDoublePref(prefs::kNTPPromoStart, | |
| 125 0, | |
| 126 PrefService::UNSYNCABLE_PREF); | |
| 127 prefs->RegisterDoublePref(prefs::kNTPPromoEnd, | |
| 128 0, | |
| 129 PrefService::UNSYNCABLE_PREF); | |
| 130 | |
| 131 prefs->RegisterIntegerPref(prefs::kNTPPromoBuild, | |
| 132 PromoResourceService::ALL_BUILDS, | |
| 133 PrefService::UNSYNCABLE_PREF); | |
| 134 prefs->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, | |
| 135 0, | |
| 136 PrefService::UNSYNCABLE_PREF); | |
| 137 prefs->RegisterIntegerPref(prefs::kNTPPromoGroupMax, | |
| 138 0, | |
| 139 PrefService::UNSYNCABLE_PREF); | |
| 140 prefs->RegisterIntegerPref(prefs::kNTPPromoViewsMax, | |
| 141 0, | |
| 142 PrefService::UNSYNCABLE_PREF); | |
| 143 | |
| 144 prefs->RegisterStringPref(prefs::kNTPPromoLine, | |
| 145 std::string(), | |
| 146 PrefService::UNSYNCABLE_PREF); | |
| 147 prefs->RegisterIntegerPref(prefs::kNTPPromoGroup, | |
| 148 0, | |
| 149 PrefService::UNSYNCABLE_PREF); | |
| 150 prefs->RegisterIntegerPref(prefs::kNTPPromoViews, | |
| 151 0, | |
| 152 PrefService::UNSYNCABLE_PREF); | |
| 153 prefs->RegisterBooleanPref(prefs::kNTPPromoClosed, | |
| 154 false, | |
| 155 PrefService::UNSYNCABLE_PREF); | |
| 156 } | |
| 157 | |
| 158 | |
| 159 void PromoNotification::WritePrefs() { | |
| 160 prefs_->SetDouble(prefs::kNTPPromoStart, start_); | |
| 161 prefs_->SetDouble(prefs::kNTPPromoEnd, end_); | |
| 162 | |
| 163 prefs_->SetInteger(prefs::kNTPPromoBuild, build_); | |
| 164 prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice, time_slice_); | |
| 165 prefs_->SetInteger(prefs::kNTPPromoGroupMax, max_group_); | |
| 166 prefs_->SetInteger(prefs::kNTPPromoViewsMax, max_views_); | |
| 167 | |
| 168 prefs_->SetString(prefs::kNTPPromoLine, text_); | |
| 169 prefs_->SetInteger(prefs::kNTPPromoGroup, group_); | |
| 170 prefs_->SetInteger(prefs::kNTPPromoViews, views_); | |
| 171 prefs_->SetBoolean(prefs::kNTPPromoClosed, closed_); | |
| 172 } | |
| 173 | |
| 174 void PromoNotification::InitFromPrefs() { | |
| 175 if (prefs_->HasPrefPath(prefs::kNTPPromoStart)) | |
| 176 start_ = prefs_->GetDouble(prefs::kNTPPromoStart); | |
| 177 | |
| 178 if (prefs_->HasPrefPath(prefs::kNTPPromoEnd)) | |
| 179 end_ = prefs_->GetDouble(prefs::kNTPPromoEnd); | |
| 180 | |
| 181 if (prefs_->HasPrefPath(prefs::kNTPPromoBuild)) | |
| 182 build_ = prefs_->GetInteger(prefs::kNTPPromoBuild); | |
| 183 | |
| 184 if (prefs_->HasPrefPath(prefs::kNTPPromoGroupTimeSlice)) | |
| 185 time_slice_ = prefs_->GetInteger(prefs::kNTPPromoGroupTimeSlice); | |
| 186 | |
| 187 if (prefs_->HasPrefPath(prefs::kNTPPromoGroupMax)) | |
| 188 max_group_ = prefs_->GetInteger(prefs::kNTPPromoGroupMax); | |
| 189 | |
| 190 if (prefs_->HasPrefPath(prefs::kNTPPromoViewsMax)) | |
| 191 max_views_ = prefs_->GetInteger(prefs::kNTPPromoViewsMax); | |
| 192 | |
| 193 if (prefs_->HasPrefPath(prefs::kNTPPromoLine)) | |
| 194 text_ = prefs_->GetString(prefs::kNTPPromoLine); | |
| 195 | |
| 196 if (prefs_->HasPrefPath(prefs::kNTPPromoGroup)) | |
| 197 group_ = prefs_->GetInteger(prefs::kNTPPromoGroup); | |
| 198 | |
| 199 if (prefs_->HasPrefPath(prefs::kNTPPromoViews)) | |
| 200 views_ = prefs_->GetInteger(prefs::kNTPPromoViews); | |
| 201 | |
| 202 if (prefs_->HasPrefPath(prefs::kNTPPromoClosed)) | |
| 203 closed_ = prefs_->GetBoolean(prefs::kNTPPromoClosed); | |
| 204 } | |
| 205 | |
| 206 bool PromoNotification::CanShow() const { | |
| 207 return !closed_ && | |
| 208 !text_.empty() && | |
| 209 group_ < max_group_ && | |
| 210 views_ < max_views_ && | |
| 211 IsBuildAllowed(build_) && | |
| 212 base::Time::FromDoubleT(StartTimeWithOffset()) < base::Time::Now() && | |
| 213 base::Time::FromDoubleT(end_) > base::Time::Now(); | |
|
Dan Beam
2011/09/27 03:27:55
Not to be a pedant, but should these be <= / >= an
achuithb
2011/09/27 08:03:19
Nope, I think the comparisons are correct as they
| |
| 214 } | |
| 215 | |
| 216 bool PromoNotification::IsBuildAllowed(int builds_allowed) const { | |
| 217 if (delegate_) // For testing. | |
| 218 return delegate_->IsBuildAllowed(builds_allowed); | |
| 219 else | |
| 220 return PromoResourceService::IsBuildTargeted( | |
| 221 PromoResourceService::GetChannel(), builds_allowed); | |
| 222 } | |
| 223 | |
| 224 double PromoNotification::StartTimeWithOffset() const { | |
| 225 // Adjust start using group and time slice, adjusted from hours to seconds. | |
| 226 static const double kSecondsInHour = 60.0 * 60.0; | |
| 227 return start_ + group_ * time_slice_ * kSecondsInHour; | |
| 228 } | |
| 229 | |
|
Dan Beam
2011/09/27 03:27:55
Should the following 3 static methods be const if
achuithb
2011/09/27 08:03:19
All static functions are const by definition, sinc
| |
| 230 // static | |
| 231 int PromoNotification::GetNextQuestionValue(const std::string& question, | |
| 232 size_t* index, | |
| 233 bool* err) { | |
| 234 if (*err) | |
| 235 return 0; | |
| 236 | |
| 237 size_t new_index = question.find(':', *index); | |
| 238 // Note that substr correctly handles npos. | |
| 239 std::string fragment(question.substr(*index, new_index - *index)); | |
| 240 *index = new_index + 1; | |
| 241 | |
| 242 int value; | |
| 243 *err = !base::StringToInt(fragment, &value); | |
| 244 return *err ? 0 : value; | |
| 245 } | |
| 246 | |
| 247 // static | |
| 248 double PromoNotification::GetTimeFromDict(const DictionaryValue* dict) { | |
| 249 std::string str; | |
| 250 dict->GetString("inproduct", &str); | |
| 251 return GetTimeFromString(str); | |
| 252 } | |
| 253 | |
|
Dan Beam
2011/09/27 03:27:55
Why are we getting these as doubles but converting
achuithb
2011/09/27 08:03:19
They are saved as doubles in the preferences file.
| |
| 254 // static | |
| 255 double PromoNotification::GetTimeFromString(const std::string& time_str) { | |
| 256 base::Time time; | |
| 257 if (time_str.empty() || !base::Time::FromString(time_str.c_str(), &time)) | |
| 258 return 0.0; | |
| 259 | |
| 260 return time.ToDoubleT(); | |
| 261 } | |
| 262 | |
| 263 double PromoNotification::GetTimeFromPrefs(const char* pref) const { | |
| 264 return prefs_->HasPrefPath(pref) ? prefs_->GetDouble(pref) : 0.0; | |
| 265 } | |
| 266 | |
| 267 bool PromoNotification::operator==(const PromoNotification& other) const { | |
| 268 return prefs_ == other.prefs_ && | |
| 269 delegate_ == other.delegate_ && | |
| 270 start_ == other.start_ && | |
| 271 end_ == other.end_ && | |
| 272 build_ == other.build_ && | |
| 273 time_slice_ == other.time_slice_ && | |
| 274 max_group_ == other.max_group_ && | |
| 275 max_views_ == other.max_views_ && | |
| 276 group_ == other.group_ && | |
| 277 views_ == other.views_ && | |
| 278 text_ == other.text_ && | |
| 279 closed_ == other.closed_; | |
| 280 } | |
| OLD | NEW |