Commit 4379e292 authored by Phillis Tang's avatar Phillis Tang Committed by Commit Bot

PWA: add custom triggering logic for install icon In-Product Help promo.

Add IPH globally scoped data and add trigger logic for install IPH that
doesn't show IPH unless site-engagement score is above 10 and IPH hasn't
been shown for this site for 90 days, and hasn't been shown globally for
14 days, and hasn't been ignored by user 3 days in a row for this site
and hasn't been ignored by user 4 days in a row globally.

Bug: 1149670
Change-Id: Idff12df63673f6e8e1fd63383f7fc57c07eb748a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2530197
Commit-Queue: Phillis Tang <phillis@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828483}
parent 63706137
......@@ -9,6 +9,8 @@
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/banners/app_banner_manager.h"
#include "chrome/browser/installable/installable_metrics.h"
......@@ -41,6 +43,11 @@ constexpr base::FeatureParam<ExperimentIcon> kInstallIconParam{
&kInstallIconExperiment, "installIcon", ExperimentIcon::kDownloadToDevice,
&kIconParamOptions};
// Site engagement score threshold to show In-Product Help.
constexpr base::FeatureParam<int> kIphSiteEngagementThresholdParam{
&feature_engagement::kIPHDesktopPwaInstallFeature,
"siteEngagementThreshold", 10};
} // namespace
PwaInstallView::PwaInstallView(
......@@ -79,7 +86,7 @@ void PwaInstallView::UpdateImpl() {
else
ResetSlideAnimation(false);
if (is_probably_promotable) {
if (is_probably_promotable && ShouldShowIph(web_contents, manager)) {
FeaturePromoControllerViews* controller =
FeaturePromoControllerViews::GetForView(this);
if (controller) {
......@@ -178,3 +185,19 @@ base::string16 PwaInstallView::GetTextForTooltipAndAccessibleName() const {
const char* PwaInstallView::GetClassName() const {
return "PwaInstallView";
}
bool PwaInstallView::ShouldShowIph(content::WebContents* web_contents,
banners::AppBannerManager* manager) {
auto start_url = manager->GetManifestStartUrl();
if (start_url.is_empty())
return false;
web_app::AppId app_id = web_app::GenerateAppIdFromURL(start_url);
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
auto score =
SiteEngagementService::Get(profile)->GetScore(web_contents->GetURL());
return score > kIphSiteEngagementThresholdParam.Get() &&
web_app::ShouldShowIph(profile->GetPrefs(), app_id);
}
......@@ -8,6 +8,10 @@
#include "base/macros.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
namespace banners {
class AppBannerManager;
} // namespace banners
// A plus icon to surface whether a site has passed PWA (progressive web app)
// installability checks and can be installed.
class PwaInstallView : public PageActionIconView {
......@@ -34,6 +38,10 @@ class PwaInstallView : public PageActionIconView {
// Track whether IPH is closed because of install icon being clicked.
bool install_icon_clicked_after_iph_shown_ = false;
// Decide whether IPH promo should be shown based on previous interactions.
bool ShouldShowIph(content::WebContents* web_contents,
banners::AppBannerManager* manager);
base::WeakPtrFactory<PwaInstallView> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(PwaInstallView);
......
......@@ -226,6 +226,15 @@ enum class InstallIphResult {
kMaxValue = kIgnored
};
// Number of times IPH can be ignored for this app before it's muted.
constexpr int kIphMuteAfterConsecutiveAppSpecificIgnores = 3;
// Number of times IPH can be ignored for any app before it's muted.
constexpr int kIphMuteAfterConsecutiveAppAgnosticIgnores = 4;
// Number of days to mute IPH after it's ignored for this app.
constexpr int kIphAppSpecificMuteTimeSpanDays = 90;
// Number of days to mute IPH after it's ignored for any app.
constexpr int kIphAppAgnosticMuteTimeSpanDays = 14;
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_CONSTANTS_H_
......@@ -6,15 +6,18 @@
#include <memory>
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_piece_forward.h"
#include "base/time/time.h"
#include "base/util/values/values_util.h"
#include "base/values.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "services/preferences/public/cpp/dictionary_value_update.h"
#include "services/preferences/public/cpp/scoped_pref_update.h"
namespace web_app {
......@@ -50,6 +53,11 @@ std::unique_ptr<prefs::DictionaryValueUpdate> UpdateWebAppDictionary(
return web_app_prefs_update;
}
// Returns whether the time occurred within X days.
bool TimeOccurredWithinDays(base::Optional<base::Time> time, int days) {
return time && (base::Time::Now() - time.value()).InDays() < days;
}
} // namespace
// The stored preferences look like:
......@@ -61,14 +69,24 @@ std::unique_ptr<prefs::DictionaryValueUpdate> UpdateWebAppDictionary(
// A double representing the number of seconds since epoch, in local time.
// Convert from/to using base::Time::FromDoubleT() and
// base::Time::ToDoubleT().
// "file_handling_origin_trial_expiry_time": 1580475600000
// "file_handling_origin_trial_expiry_time": 1580475600000,
// "IPH_num_of_consecutive_ignore": 2,
// A string-flavored base::value representing the int64_t number of
// microseconds since the Windows epoch, using util::TimeToValue().
// "IPH_last_ignore_time": "13249617864945580",
// },
// "<app_id_N>": {
// "was_external_app_uninstalled_by_user": false,
// "file_handlers_enabled": false,
// "file_handling_origin_trial_expiry_time": 0
// }
// }
// },
// "app_agnostic_iph_state": {
// "IPH_num_of_consecutive_ignore": 3,
// A string-flavored base::Value representing int64_t number of microseconds
// since the Windows epoch, using util::TimeToValue().
// "IPH_last_ignore_time": "13249617864945500",
// },
// }
//
const char kWasExternalAppUninstalledByUser[] =
......@@ -90,6 +108,7 @@ const char kIphLastIgnoreTime[] = "IPH_last_ignore_time";
void WebAppPrefsUtilsRegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterDictionaryPref(::prefs::kWebAppsPreferences);
registry->RegisterDictionaryPref(::prefs::kWebAppsAppAgnosticIphState);
}
bool GetBoolWebAppPref(const PrefService* pref_service,
......@@ -196,11 +215,20 @@ void RemoveWebAppPref(PrefService* pref_service,
void RecordInstallIphIgnored(PrefService* pref_service, const AppId& app_id) {
base::Optional<int> ignored_count =
GetIntWebAppPref(pref_service, app_id, kIphIgnoreCount);
int new_count = 1 + ignored_count.value_or(0);
int new_count = base::saturated_cast<int>(1 + ignored_count.value_or(0));
UpdateIntWebAppPref(pref_service, app_id, kIphIgnoreCount, new_count);
UpdateTimeWebAppPref(pref_service, app_id, kIphLastIgnoreTime,
base::Time::Now());
prefs::ScopedDictionaryPrefUpdate update(pref_service,
prefs::kWebAppsAppAgnosticIphState);
int global_count = 0;
update->GetInteger(kIphIgnoreCount, &global_count);
update->SetInteger(kIphIgnoreCount,
base::saturated_cast<int>(global_count + 1));
update->Set(kIphLastIgnoreTime, std::make_unique<base::Value>(
util::TimeToValue(base::Time::Now())));
}
void RecordInstallIphInstalled(PrefService* pref_service, const AppId& app_id) {
......@@ -208,6 +236,40 @@ void RecordInstallIphInstalled(PrefService* pref_service, const AppId& app_id) {
// ignoring IPH, to help determine when IPH should be muted. Therefore
// resetting ignored count on successful install.
UpdateIntWebAppPref(pref_service, app_id, kIphIgnoreCount, 0);
prefs::ScopedDictionaryPrefUpdate update(pref_service,
prefs::kWebAppsAppAgnosticIphState);
update->SetInteger(kIphIgnoreCount, 0);
}
bool ShouldShowIph(PrefService* pref_service, const AppId& app_id) {
// Do not show IPH if the user ignored the last N+ promos for this app.
int app_ignored_count =
GetIntWebAppPref(pref_service, app_id, kIphIgnoreCount).value_or(0);
if (app_ignored_count >= kIphMuteAfterConsecutiveAppSpecificIgnores)
return false;
// Do not show IPH if the user ignored a promo for this app within N days.
auto app_last_ignore =
GetTimeWebAppPref(pref_service, app_id, kIphLastIgnoreTime);
if (TimeOccurredWithinDays(app_last_ignore,
kIphAppSpecificMuteTimeSpanDays)) {
return false;
}
auto* dict = pref_service->GetDictionary(prefs::kWebAppsAppAgnosticIphState);
// Do not show IPH if the user ignored the last N+ promos for any app.
int global_ignored_count = dict->FindIntKey(kIphIgnoreCount).value_or(0);
if (global_ignored_count >= kIphMuteAfterConsecutiveAppAgnosticIgnores)
return false;
// Do not show IPH if the user ignored a promo for any app within N days.
auto global_last_ignore =
util::ValueToTime(dict->FindKey(kIphLastIgnoreTime));
if (TimeOccurredWithinDays(global_last_ignore,
kIphAppAgnosticMuteTimeSpanDays)) {
return false;
}
return true;
}
} // namespace web_app
......@@ -79,6 +79,10 @@ void RecordInstallIphIgnored(PrefService* pref_service, const AppId& app_id);
void RecordInstallIphInstalled(PrefService* pref_service, const AppId& app_id);
// Returns whether Web App Install In Product Help should be shown based on
// previous interactions with this promo.
bool ShouldShowIph(PrefService* pref_service, const AppId& app_id);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_PREFS_UTILS_H_
......@@ -1922,6 +1922,9 @@ const char kWebAppsDailyMetricsDate[] = "web_apps.daily_metrics_date";
// Dictionary that maps web app URLs to Chrome extension IDs.
const char kWebAppsExtensionIDs[] = "web_apps.extension_ids";
// Dictionary that stores IPH state not scoped to a particular app.
const char kWebAppsAppAgnosticIphState[] = "web_apps.app_agnostic_iph_state";
// A string representing the last version of Chrome preinstalled web apps were
// synchronised for.
const char kWebAppsLastPreinstallSynchronizeVersion[] =
......
......@@ -647,6 +647,7 @@ extern const char kWebAppInstallMetrics[];
extern const char kWebAppsDailyMetrics[];
extern const char kWebAppsDailyMetricsDate[];
extern const char kWebAppsExtensionIDs[];
extern const char kWebAppsAppAgnosticIphState[];
extern const char kWebAppsLastPreinstallSynchronizeVersion[];
extern const char kWebAppsPreferences[];
extern const char kWebAppsUserDisplayModeCleanedUp[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment