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