1 // Copyright 2014 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 CHROME_BROWSER_BANNERS_APP_BANNER_SETTINGS_HELPER_H_
6 #define CHROME_BROWSER_BANNERS_APP_BANNER_SETTINGS_HELPER_H_
7 
8 #include <set>
9 #include <string>
10 
11 #include "base/macros.h"
12 #include "base/optional.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/installable/installable_logging.h"
15 
16 namespace content {
17 class WebContents;
18 }  // namespace content
19 
20 class GURL;
21 class Profile;
22 
23 // Utility class to record banner events for the given package or start url.
24 //
25 // These events are used to decide when banners should be shown, using a
26 // heuristic based on how many different days in a recent period of time (for
27 // example the past two weeks) the banner could have been shown, when it was
28 // last shown, when it was last blocked, and when it was last installed (for
29 // ServiceWorker style apps - native apps can query whether the app was
30 // installed directly).
31 //
32 // The desired effect is to have banners appear once a user has demonstrated
33 // an ongoing relationship with the app, and not to pester the user too much.
34 //
35 // For most events only the last event is recorded. The exception are the
36 // could show events. For these a list of the events is maintained. At most
37 // one event is stored per day, and events outside the window the heuristic
38 // uses are discarded. Local times are used to enforce these rules, to ensure
39 // what we count as a day matches what the user perceives to be days.
40 class AppBannerSettingsHelper {
41  public:
42   // An enum containing possible app menu verbiage for installing a web app.
43   // A Java counterpart will be generated for this enum.
44   // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.banners
45   enum AppMenuVerbiage {
46     APP_MENU_OPTION_UNKNOWN = 0,
47     APP_MENU_OPTION_MIN = APP_MENU_OPTION_UNKNOWN,
48     APP_MENU_OPTION_ADD_TO_HOMESCREEN = 1,
49     APP_MENU_OPTION_INSTALL = 2,
50     APP_MENU_OPTION_MAX = APP_MENU_OPTION_INSTALL,
51   };
52 
53   // The various types of banner events recorded as timestamps in the app banner
54   // content setting per origin and package_name_or_start_url pair. This enum
55   // corresponds to the kBannerEventsKeys array.
56   // TODO(mariakhomenko): Rename events to reflect that they are used in more
57   // contexts now.
58   enum AppBannerEvent {
59     // Records the first time that a site met the conditions to show a banner.
60     // Used for computing the MinutesFromFirstVisitToBannerShown metric.
61     APP_BANNER_EVENT_COULD_SHOW,
62     // Records the latest time a banner was shown to the user. Used to suppress
63     // the banner from being shown too often.
64     APP_BANNER_EVENT_DID_SHOW,
65     // Records the latest time a banner was dismissed by the user. Used to
66     // suppress the banner for some time if the user explicitly didn't want it.
67     APP_BANNER_EVENT_DID_BLOCK,
68     // Records the latest time the user added a site to the homescreen from a
69     // banner, or launched that site from homescreen. Used to ensure banners are
70     // not shown for sites which were added, and to determine if sites were
71     // launched recently.
72     APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
73     APP_BANNER_EVENT_NUM_EVENTS,
74   };
75 
76   static const char kInstantAppsKey[];
77 
78   // The content setting basically records a simplified subset of history.
79   // For privacy reasons this needs to be cleared. The ClearHistoryForURLs
80   // function removes any information from the banner content settings for the
81   // given URls.
82   static void ClearHistoryForURLs(Profile* profile,
83                                   const std::set<GURL>& origin_urls);
84 
85   // Record a banner installation event, for either a WEB or NATIVE app.
86   static void RecordBannerInstallEvent(
87       content::WebContents* web_contents,
88       const std::string& package_name_or_start_url);
89 
90   // Record a banner dismissal event, for either a WEB or NATIVE app.
91   static void RecordBannerDismissEvent(
92       content::WebContents* web_contents,
93       const std::string& package_name_or_start_url);
94 
95   // Record a banner event specified by |event|.
96   static void RecordBannerEvent(content::WebContents* web_contents,
97                                 const GURL& origin_url,
98                                 const std::string& package_name_or_start_url,
99                                 AppBannerEvent event,
100                                 base::Time time);
101 
102   // Reports whether the app install banner was blocked by the user recently
103   // enough with respect to |now| that another banner should not yet be shown.
104   // |package_name_or_start_url| must be non-empty.
105   static bool WasBannerRecentlyBlocked(
106       content::WebContents* web_contents,
107       const GURL& origin_url,
108       const std::string& package_name_or_start_url,
109       base::Time now);
110 
111   // Reports whether the app install banner was ignored by the user recently
112   // enough with respect to |now| that another banner should not yet be shown.
113   // |package_name_or_start_url| must be non-empty.
114   static bool WasBannerRecentlyIgnored(
115       content::WebContents* web_contents,
116       const GURL& origin_url,
117       const std::string& package_name_or_start_url,
118       base::Time now);
119 
120   // Returns whether the supplied app has ever been installed from |origin_url|.
121   static bool HasBeenInstalled(content::WebContents* web_contents,
122                                const GURL& origin_url,
123                                const std::string& package_name_or_start_url);
124 
125   // Get the time that |event| was recorded, or a nullopt if it no dict to
126   // record yet(such as exceed max num per site) . Exposed for testing.
127   static base::Optional<base::Time> GetSingleBannerEvent(
128       content::WebContents* web_contents,
129       const GURL& origin_url,
130       const std::string& package_name_or_start_url,
131       AppBannerEvent event);
132 
133   // Returns true if |total_engagement| is sufficiently high to warrant
134   // triggering a banner, or if the command-line flag to bypass engagement
135   // checking is true.
136   static bool HasSufficientEngagement(double total_engagement);
137 
138   // Record a UMA statistic measuring the minutes between the first visit to the
139   // site and the first showing of the banner.
140   static void RecordMinutesFromFirstVisitToShow(
141       content::WebContents* web_contents,
142       const GURL& origin_url,
143       const std::string& package_name_or_start_url,
144       base::Time time);
145 
146   // Returns true if any site under |origin| was launched from homescreen in the
147   // last ten days. This allows services outside app banners to utilise the
148   // content setting that ensures app banners are not shown for sites which ave
149   // already been added to homescreen.
150   static bool WasLaunchedRecently(Profile* profile,
151                                   const GURL& origin_url,
152                                   base::Time now);
153 
154   // Set the number of days which dismissing/ignoring the banner should prevent
155   // a banner from showing.
156   static void SetDaysAfterDismissAndIgnoreToTrigger(unsigned int dismiss_days,
157                                                     unsigned int ignore_days);
158 
159   // Set the total engagement weight required to trigger a banner.
160   static void SetTotalEngagementToTrigger(double total_engagement);
161 
162   // Resets the engagement weights, minimum minutes, and total engagement to
163   // trigger to their default values.
164   static void SetDefaultParameters();
165 
166   // Updates all values from field trial.
167   static void UpdateFromFieldTrial();
168 
169   // Returns whether we are out of |scope|'s animation suppression period and
170   // can show an animation.
171   static bool CanShowInstallTextAnimation(content::WebContents* web_contents,
172                                           const GURL& scope);
173 
174   // Records the fact that we've shown an animation for |scope| and updates its
175   // animation suppression period.
176   static void RecordInstallTextAnimationShown(
177       content::WebContents* web_contents,
178       const GURL& scope);
179 
180   // Utility class for testing, which sets how long the banner should be
181   // suppressed after it is dismissed or ignored. The previous configuration
182   // is restored when this object is destructed.
183   class ScopedTriggerSettings {
184    public:
185     ScopedTriggerSettings(unsigned int dismiss_days, unsigned int ignore_days);
186     virtual ~ScopedTriggerSettings();
187 
188    private:
189     unsigned int old_dismiss_, old_ignore_;
190     DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedTriggerSettings);
191   };
192 
193  private:
194   DISALLOW_IMPLICIT_CONSTRUCTORS(AppBannerSettingsHelper);
195 };
196 
197 #endif  // CHROME_BROWSER_BANNERS_APP_BANNER_SETTINGS_HELPER_H_
198