Commit eec2194c authored by Daniel Park's avatar Daniel Park Committed by Commit Bot

[Chrome Home Survey] Adds client-side filtering

-Reorders order for doesUserQualify
-Adds filtering methods
-Adds tests for filtering methods
-Adds logging for when the last time the random # gen. was rolled
-Adds Chrome Home Force Survey check to doesUserQualify to allow for
  testing of other survey qualifications

Filtering method:
-Gets the max number from the finch config
-Rolls a random number up from 0 (inclusive) to that number (exclusive)
-If the random number = 0, process continues for user
-Else stop


Bug: 778384
Change-Id: Ie7063724614c98121e0e93c1a4b091557635015a
Reviewed-on: https://chromium-review.googlesource.com/741867
Commit-Queue: Daniel Park <danielpark@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#519836}
parent 5650735a
...@@ -6,12 +6,13 @@ package org.chromium.chrome.browser.survey; ...@@ -6,12 +6,13 @@ package org.chromium.chrome.browser.survey;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.text.TextUtils; import android.text.TextUtils;
import org.chromium.base.CommandLine; import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.StrictModeContext; import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
...@@ -31,6 +32,9 @@ import org.chromium.content_public.browser.WebContents; ...@@ -31,6 +32,9 @@ import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver; import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.DeviceFormFactor;
import java.util.Calendar;
import java.util.Random;
/** /**
* Class that controls if and when to show surveys related to the Chrome Home experiment. * Class that controls if and when to show surveys related to the Chrome Home experiment.
*/ */
...@@ -40,13 +44,16 @@ public class ChromeHomeSurveyController { ...@@ -40,13 +44,16 @@ public class ChromeHomeSurveyController {
public static final String PARAM_NAME = "survey_override_site_id"; public static final String PARAM_NAME = "survey_override_site_id";
private static final String TRIAL_NAME = "ChromeHome"; private static final String TRIAL_NAME = "ChromeHome";
private static final String MAX_NUMBER = "MaxNumber";
static final long ONE_WEEK_IN_MILLIS = 604800000L; static final long ONE_WEEK_IN_MILLIS = 604800000L;
static final String SURVEY_INFOBAR_DISMISSED_KEY = "chrome_home_survey_info_bar_dismissed"; static final String SURVEY_INFOBAR_DISMISSED_KEY = "chrome_home_survey_info_bar_dismissed";
static final String DATE_LAST_ROLLED_KEY = "chrome_home_date_last_rolled_for_survey";
private TabModelSelector mTabModelSelector; private TabModelSelector mTabModelSelector;
private ChromeHomeSurveyController() { @VisibleForTesting
ChromeHomeSurveyController() {
// Empty constructor. // Empty constructor.
} }
...@@ -57,12 +64,11 @@ public class ChromeHomeSurveyController { ...@@ -57,12 +64,11 @@ public class ChromeHomeSurveyController {
* shown. * shown.
*/ */
public static void initialize(Context context, TabModelSelector tabModelSelector) { public static void initialize(Context context, TabModelSelector tabModelSelector) {
new ChromeHomeSurveyController().startDownload(context, tabModelSelector); new StartDownloadIfEligibleTask(new ChromeHomeSurveyController(), context, tabModelSelector)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void startDownload(Context context, TabModelSelector tabModelSelector) { private void startDownload(Context context, TabModelSelector tabModelSelector) {
if (!doesUserQualifyForSurvey()) return;
mTabModelSelector = tabModelSelector; mTabModelSelector = tabModelSelector;
SurveyController surveyController = SurveyController.getInstance(); SurveyController surveyController = SurveyController.getInstance();
...@@ -86,15 +92,14 @@ public class ChromeHomeSurveyController { ...@@ -86,15 +92,14 @@ public class ChromeHomeSurveyController {
} }
private boolean doesUserQualifyForSurvey() { private boolean doesUserQualifyForSurvey() {
if (CommandLine.getInstance().hasSwitch(ChromeSwitches.CHROME_HOME_FORCE_ENABLE_SURVEY)) {
return true;
}
if (DeviceFormFactor.isTablet()) return false; if (DeviceFormFactor.isTablet()) return false;
if (!isUMAEnabled()) return false; if (!isUMAEnabled()) return false;
if (AccessibilityUtil.isAccessibilityEnabled()) return false; if (AccessibilityUtil.isAccessibilityEnabled()) return false;
if (hasInfoBarBeenDisplayed()) return false; if (hasInfoBarBeenDisplayed()) return false;
if (!FeatureUtilities.isChromeHomeEnabled()) return true; if (!FeatureUtilities.isChromeHomeEnabled()) return true;
return wasChromeHomeEnabledForMinimumOneWeek(); return wasChromeHomeEnabledForMinimumOneWeek()
|| CommandLine.getInstance().hasSwitch(
ChromeSwitches.CHROME_HOME_FORCE_ENABLE_SURVEY);
} }
private void onSurveyAvailable(String siteId) { private void onSurveyAvailable(String siteId) {
...@@ -133,35 +138,30 @@ public class ChromeHomeSurveyController { ...@@ -133,35 +138,30 @@ public class ChromeHomeSurveyController {
} }
private boolean isUMAEnabled() { private boolean isUMAEnabled() {
try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { return PrivacyPreferencesManager.getInstance().isUsageAndCrashReportingPermittedByUser();
return PrivacyPreferencesManager.getInstance()
.isUsageAndCrashReportingPermittedByUser();
}
} }
private void showSurveyInfoBar(WebContents webContents, String siteId) { private void showSurveyInfoBar(WebContents webContents, String siteId) {
SurveyInfoBar.showSurveyInfoBar( SurveyInfoBar.showSurveyInfoBar(
webContents, siteId, true, R.drawable.chrome_sync_logo, getSurveyInfoBarDelegate()); webContents, siteId, true, R.drawable.chrome_sync_logo, getSurveyInfoBarDelegate());
SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences(); SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences();
sharedPreferences.edit().putBoolean(SURVEY_INFO_BAR_DISPLAYED_KEY, true).apply(); sharedPreferences.edit()
.putLong(SURVEY_INFO_BAR_DISPLAYED_KEY, System.currentTimeMillis())
.apply();
} }
@VisibleForTesting @VisibleForTesting
boolean hasInfoBarBeenDisplayed() { boolean hasInfoBarBeenDisplayed() {
try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences(); SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences();
return sharedPreferences.getBoolean(SURVEY_INFO_BAR_DISPLAYED_KEY, false); return sharedPreferences.getLong(SURVEY_INFO_BAR_DISPLAYED_KEY, -1L) != -1L;
}
} }
@VisibleForTesting @VisibleForTesting
boolean wasChromeHomeEnabledForMinimumOneWeek() { boolean wasChromeHomeEnabledForMinimumOneWeek() {
try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences(); SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences();
long earliestLoggedDate = sharedPreferences.getLong( long earliestLoggedDate = sharedPreferences.getLong(
ChromePreferenceManager.CHROME_HOME_SHARED_PREFERENCES_KEY, Long.MAX_VALUE); ChromePreferenceManager.CHROME_HOME_SHARED_PREFERENCES_KEY, Long.MAX_VALUE);
if (System.currentTimeMillis() - earliestLoggedDate >= ONE_WEEK_IN_MILLIS) return true; if (System.currentTimeMillis() - earliestLoggedDate >= ONE_WEEK_IN_MILLIS) return true;
}
return false; return false;
} }
...@@ -175,6 +175,52 @@ public class ChromeHomeSurveyController { ...@@ -175,6 +175,52 @@ public class ChromeHomeSurveyController {
return new ChromeHomeSurveyController(); return new ChromeHomeSurveyController();
} }
/**
* Rolls a random number to see if the user was eligible for the survey
* @return Whether the user is eligible (i.e. the random number rolled was 0).
*/
@VisibleForTesting
boolean isRandomlySelectedForSurvey() {
SharedPreferences preferences = ContextUtils.getAppSharedPreferences();
int lastDate = preferences.getInt(DATE_LAST_ROLLED_KEY, -1);
int today = getDayOfYear();
if (lastDate == today) return false;
int maxNumber = getMaxNumber();
if (maxNumber == -1) return false;
preferences.edit().putInt(DATE_LAST_ROLLED_KEY, today).apply();
return getRandomNumberUpTo(maxNumber) == 0;
}
/**
* @param max The max threshold for the random number generator.
* @return A random number from 0 (inclusive) to the max number (exclusive).
*/
@VisibleForTesting
int getRandomNumberUpTo(int max) {
return new Random().nextInt(max);
}
/** @return The max number as stated in the finch config. */
@VisibleForTesting
int getMaxNumber() {
try {
String number = VariationsAssociatedData.getVariationParamValue(TRIAL_NAME, MAX_NUMBER);
if (TextUtils.isEmpty(number)) return -1;
return Integer.parseInt(number);
} catch (NumberFormatException e) {
return -1;
}
}
/** @return The day of the year for today. */
@VisibleForTesting
int getDayOfYear() {
ThreadUtils.assertOnBackgroundThread();
return Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
}
/** /**
* @return The survey info bar delegate containing actions specific to the Chrome Home survey. * @return The survey info bar delegate containing actions specific to the Chrome Home survey.
*/ */
...@@ -199,4 +245,30 @@ public class ChromeHomeSurveyController { ...@@ -199,4 +245,30 @@ public class ChromeHomeSurveyController {
} }
}; };
} }
static class StartDownloadIfEligibleTask extends AsyncTask<Void, Void, Boolean> {
final ChromeHomeSurveyController mController;
final Context mContext;
final TabModelSelector mSelector;
public StartDownloadIfEligibleTask(ChromeHomeSurveyController controller, Context context,
TabModelSelector tabModelSelector) {
mController = controller;
mContext = context;
mSelector = tabModelSelector;
}
@Override
protected Boolean doInBackground(Void... params) {
if (!mController.doesUserQualifyForSurvey()) return false;
return mController.isRandomlySelectedForSurvey()
|| CommandLine.getInstance().hasSwitch(
ChromeSwitches.CHROME_HOME_FORCE_ENABLE_SURVEY);
}
@Override
protected void onPostExecute(Boolean result) {
if (result) mController.startDownload(mContext, mSelector);
}
}
} }
...@@ -59,11 +59,12 @@ public class ChromeHomeSurveyControllerTest { ...@@ -59,11 +59,12 @@ public class ChromeHomeSurveyControllerTest {
@Test @Test
public void testInfoBarDisplayedBefore() { public void testInfoBarDisplayedBefore() {
Assert.assertFalse(mController.hasInfoBarBeenDisplayed());
Assert.assertFalse(mSharedPreferences.contains( Assert.assertFalse(mSharedPreferences.contains(
ChromeHomeSurveyController.SURVEY_INFO_BAR_DISPLAYED_KEY)); ChromeHomeSurveyController.SURVEY_INFO_BAR_DISPLAYED_KEY));
Assert.assertFalse(mController.hasInfoBarBeenDisplayed());
mSharedPreferences.edit() mSharedPreferences.edit()
.putBoolean(ChromeHomeSurveyController.SURVEY_INFO_BAR_DISPLAYED_KEY, true) .putLong(ChromeHomeSurveyController.SURVEY_INFO_BAR_DISPLAYED_KEY,
System.currentTimeMillis())
.apply(); .apply();
Assert.assertTrue(mController.hasInfoBarBeenDisplayed()); Assert.assertTrue(mController.hasInfoBarBeenDisplayed());
} }
...@@ -123,4 +124,68 @@ public class ChromeHomeSurveyControllerTest { ...@@ -123,4 +124,68 @@ public class ChromeHomeSurveyControllerTest {
verify(mTab, times(1)).getWebContents(); verify(mTab, times(1)).getWebContents();
verify(mTab, never()).isIncognito(); verify(mTab, never()).isIncognito();
} }
@Test
public void testEligibilityRolledYesterday() {
mController = new RiggedSurveyController(0, 5, 10);
mSharedPreferences.edit().putInt(ChromeHomeSurveyController.DATE_LAST_ROLLED_KEY, 4);
Assert.assertTrue(mController.isRandomlySelectedForSurvey());
}
@Test
public void testEligibilityRollingTwiceSameDay() {
mController = new RiggedSurveyController(0, 5, 10);
mSharedPreferences.edit()
.putInt(ChromeHomeSurveyController.DATE_LAST_ROLLED_KEY, 5)
.apply();
Assert.assertFalse(mController.isRandomlySelectedForSurvey());
}
@Test
public void testEligibilityFirstTimeRollingQualifies() {
mController = new RiggedSurveyController(0, 5, 10);
Assert.assertFalse(
mSharedPreferences.contains(ChromeHomeSurveyController.DATE_LAST_ROLLED_KEY));
Assert.assertTrue(mController.isRandomlySelectedForSurvey());
Assert.assertEquals(
5, mSharedPreferences.getInt(ChromeHomeSurveyController.DATE_LAST_ROLLED_KEY, -1));
}
@Test
public void testEligibilityFirstTimeRollingDoesNotQualify() {
mController = new RiggedSurveyController(5, 1, 10);
Assert.assertFalse(
mSharedPreferences.contains(ChromeHomeSurveyController.DATE_LAST_ROLLED_KEY));
Assert.assertFalse(mController.isRandomlySelectedForSurvey());
Assert.assertEquals(
1, mSharedPreferences.getInt(ChromeHomeSurveyController.DATE_LAST_ROLLED_KEY, -1));
}
class RiggedSurveyController extends ChromeHomeSurveyController {
int mRandomNumberToReturn;
int mDayOfYear;
int mMaxNumber;
RiggedSurveyController(int randomNumberToReturn, int dayOfYear, int maxNumber) {
super();
mRandomNumberToReturn = randomNumberToReturn;
mDayOfYear = dayOfYear;
mMaxNumber = maxNumber;
}
@Override
int getRandomNumberUpTo(int max) {
return mRandomNumberToReturn;
}
@Override
int getDayOfYear() {
return mDayOfYear;
}
@Override
int getMaxNumber() {
return mMaxNumber;
}
}
} }
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