1 // Copyright 2016 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 #ifndef COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
6 #define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
7 
8 #include <memory>
9 #include <vector>
10 
11 #include "base/macros.h"
12 #include "base/time/clock.h"
13 #include "base/time/time.h"
14 #include "components/ntp_snippets/category.h"
15 #include "components/ntp_snippets/category_rankers/category_ranker.h"
16 
17 class PrefRegistrySimple;
18 class PrefService;
19 
20 namespace ntp_snippets {
21 
22 // An implementation of a CategoryRanker based on a number of clicks per
23 // category. Initial order is hardcoded, but sections with more clicks are moved
24 // to the top. The new remote categories must be registered using
25 // AppendCategoryIfNecessary. All other categories must be hardcoded in the
26 // initial order. The order and category usage data are persisted in prefs and
27 // reloaded on startup.
28 // TODO(crbug.com/675929): Remove unused categories from prefs.
29 class ClickBasedCategoryRanker : public CategoryRanker {
30  public:
31   explicit ClickBasedCategoryRanker(PrefService* pref_service,
32                                     base::Clock* clock);
33   ~ClickBasedCategoryRanker() override;
34 
35   // CategoryRanker implementation.
36   bool Compare(Category left, Category right) const override;
37   void ClearHistory(base::Time begin, base::Time end) override;
38   void AppendCategoryIfNecessary(Category category) override;
39   void InsertCategoryBeforeIfNecessary(Category category_to_insert,
40                                        Category anchor) override;
41   void InsertCategoryAfterIfNecessary(Category category_to_insert,
42                                       Category anchor) override;
43   std::vector<CategoryRanker::DebugDataItem> GetDebugData() override;
44   void OnSuggestionOpened(Category category) override;
45   void OnCategoryDismissed(Category category) override;
46 
47   // Returns time when last decay occured. For testing only.
48   base::Time GetLastDecayTime() const;
49 
50   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
51 
52   // Returns passing margin, i.e. a number of extra clicks required to move a
53   // category upwards. For testing only.
54   static int GetPassingMargin();
55 
56   // Returns number of top categories with extra margin (i.e. with increased
57   // passing margin). For testing only.
58   static int GetNumTopCategoriesWithExtraMargin();
59 
60   // Returns number of positions by which a dismissed category is downgraded.
61   // For testing only.
62   static int GetDismissedCategoryPenalty();
63 
64  private:
65   struct RankedCategory {
66     Category category;
67     int clicks;
68     base::Time last_dismissed;
69 
70     RankedCategory(Category category,
71                    int clicks,
72                    const base::Time& last_dismissed);
73   };
74 
75   int GetPositionPassingMargin(
76       std::vector<RankedCategory>::const_iterator category_position) const;
77   void RestoreDefaultOrder();
78   void AppendKnownCategory(KnownCategories known_category);
79   bool ReadOrderFromPrefs(std::vector<RankedCategory>* result_categories) const;
80   void StoreOrderToPrefs(const std::vector<RankedCategory>& ordered_categories);
81   std::vector<RankedCategory>::iterator FindCategory(Category category);
82   bool ContainsCategory(Category category) const;
83   void InsertCategoryRelativeToIfNecessary(Category category_to_insert,
84                                            Category anchor,
85                                            bool after);
86 
87   base::Time ReadLastDecayTimeFromPrefs() const;
88   void StoreLastDecayTimeToPrefs(base::Time last_decay_time);
89   bool IsEnoughClicksToDecay() const;
90   bool DecayClicksIfNeeded();
91 
92   std::vector<RankedCategory> ordered_categories_;
93   PrefService* pref_service_;
94   base::Clock* clock_;
95 
96   DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRanker);
97 };
98 
99 }  // namespace ntp_snippets
100 
101 #endif  // COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
102