1 // Copyright 2020 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 package org.chromium.chrome.features.start_surface; 6 7 import androidx.annotation.VisibleForTesting; 8 9 import org.chromium.base.Log; 10 import org.chromium.base.SysUtils; 11 import org.chromium.base.metrics.RecordHistogram; 12 import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter; 13 import org.chromium.chrome.browser.flags.CachedFeatureFlags; 14 import org.chromium.chrome.browser.flags.ChromeFeatureList; 15 import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter; 16 import org.chromium.chrome.browser.flags.StringCachedFieldTrialParameter; 17 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; 18 import org.chromium.chrome.browser.preferences.Pref; 19 import org.chromium.chrome.browser.preferences.PrefChangeRegistrar; 20 import org.chromium.chrome.browser.preferences.SharedPreferencesManager; 21 import org.chromium.chrome.browser.profiles.Profile; 22 import org.chromium.components.user_prefs.UserPrefs; 23 24 /** 25 * Flag configuration for Start Surface. Source of truth for whether it should be enabled and 26 * which variation should be used. 27 */ 28 public class StartSurfaceConfiguration { 29 private static final String TAG = "StartSurfaceConfig"; 30 public static final StringCachedFieldTrialParameter START_SURFACE_VARIATION = 31 new StringCachedFieldTrialParameter( 32 ChromeFeatureList.START_SURFACE_ANDROID, "start_surface_variation", ""); 33 public static final BooleanCachedFieldTrialParameter START_SURFACE_EXCLUDE_MV_TILES = 34 new BooleanCachedFieldTrialParameter( 35 ChromeFeatureList.START_SURFACE_ANDROID, "exclude_mv_tiles", false); 36 public static final BooleanCachedFieldTrialParameter 37 START_SURFACE_HIDE_INCOGNITO_SWITCH_NO_TAB = 38 new BooleanCachedFieldTrialParameter(ChromeFeatureList.START_SURFACE_ANDROID, 39 "hide_switch_when_no_incognito_tabs", false); 40 41 // This parameter hides the incognito switch on non-incognito Start Surface homepage but still 42 // shows the switch in Grid tab switcher. It is different from 43 // {@link START_SURFACE_HIDE_INCOGNITO_SWITCH_NO_TAB} which hides the incognito switch in both 44 // Start Surface homepage and Grid tab switcher whenever there is no incognito tab. 45 public static final BooleanCachedFieldTrialParameter START_SURFACE_HIDE_INCOGNITO_SWITCH = 46 new BooleanCachedFieldTrialParameter( 47 ChromeFeatureList.START_SURFACE_ANDROID, "hide_incognito_switch", false); 48 49 public static final BooleanCachedFieldTrialParameter START_SURFACE_LAST_ACTIVE_TAB_ONLY = 50 new BooleanCachedFieldTrialParameter( 51 ChromeFeatureList.START_SURFACE_ANDROID, "show_last_active_tab_only", false); 52 public static final BooleanCachedFieldTrialParameter START_SURFACE_SHOW_STACK_TAB_SWITCHER = 53 new BooleanCachedFieldTrialParameter( 54 ChromeFeatureList.START_SURFACE_ANDROID, "show_stack_tab_switcher", false); 55 public static final BooleanCachedFieldTrialParameter START_SURFACE_OPEN_NTP_INSTEAD_OF_START = 56 new BooleanCachedFieldTrialParameter( 57 ChromeFeatureList.START_SURFACE_ANDROID, "open_ntp_instead_of_start", false); 58 public static final StringCachedFieldTrialParameter START_SURFACE_OMNIBOX_SCROLL_MODE = 59 new StringCachedFieldTrialParameter( 60 ChromeFeatureList.START_SURFACE_ANDROID, "omnibox_scroll_mode", ""); 61 62 private static final String TRENDY_ENABLED_PARAM = "trendy_enabled"; 63 public static final BooleanCachedFieldTrialParameter TRENDY_ENABLED = 64 new BooleanCachedFieldTrialParameter( 65 ChromeFeatureList.START_SURFACE_ANDROID, TRENDY_ENABLED_PARAM, false); 66 67 private static final String SUCCESS_MIN_PERIOD_MS_PARAM = "trendy_success_min_period_ms"; 68 public static final IntCachedFieldTrialParameter TRENDY_SUCCESS_MIN_PERIOD_MS = 69 new IntCachedFieldTrialParameter(ChromeFeatureList.START_SURFACE_ANDROID, 70 SUCCESS_MIN_PERIOD_MS_PARAM, 86400_000); 71 72 private static final String FAILURE_MIN_PERIOD_MS_PARAM = "trendy_failure_min_period_ms"; 73 public static final IntCachedFieldTrialParameter TRENDY_FAILURE_MIN_PERIOD_MS = 74 new IntCachedFieldTrialParameter( 75 ChromeFeatureList.START_SURFACE_ANDROID, FAILURE_MIN_PERIOD_MS_PARAM, 7200_000); 76 77 private static final String TRENDY_ENDPOINT_PARAM = "trendy_endpoint"; 78 public static final StringCachedFieldTrialParameter TRENDY_ENDPOINT = 79 new StringCachedFieldTrialParameter(ChromeFeatureList.START_SURFACE_ANDROID, 80 TRENDY_ENDPOINT_PARAM, 81 "https://trends.google.com/trends/trendingsearches/daily/rss" 82 + "?lite=true&safe=true&geo="); 83 84 private static final String STARTUP_UMA_PREFIX = "Startup.Android."; 85 private static final String INSTANT_START_SUBFIX = ".Instant"; 86 private static final String REGULAR_START_SUBFIX = ".NoInstant"; 87 88 /** 89 * @return Whether the Start Surface is enabled. 90 */ isStartSurfaceEnabled()91 public static boolean isStartSurfaceEnabled() { 92 return CachedFeatureFlags.isEnabled(ChromeFeatureList.START_SURFACE_ANDROID) 93 && !SysUtils.isLowEndDevice(); 94 } 95 96 /** 97 * @return Whether the Start Surface SinglePane is enabled. 98 */ isStartSurfaceSinglePaneEnabled()99 public static boolean isStartSurfaceSinglePaneEnabled() { 100 return isStartSurfaceEnabled() && START_SURFACE_VARIATION.getValue().equals("single"); 101 } 102 103 /** 104 *@return Whether the Start Surface Stack Tab Switcher is enabled. 105 */ isStartSurfaceStackTabSwitcherEnabled()106 public static boolean isStartSurfaceStackTabSwitcherEnabled() { 107 return isStartSurfaceSinglePaneEnabled() 108 && START_SURFACE_SHOW_STACK_TAB_SWITCHER.getValue(); 109 } 110 111 /** 112 * Add an observer to keep {@link ChromePreferenceKeys.FEED_ARTICLES_LIST_VISIBLE} consistent 113 * with {@link Pref.ARTICLES_LIST_VISIBLE}. 114 */ addFeedVisibilityObserver()115 public static void addFeedVisibilityObserver() { 116 updateFeedVisibility(); 117 PrefChangeRegistrar prefChangeRegistrar = new PrefChangeRegistrar(); 118 prefChangeRegistrar.addObserver( 119 Pref.ARTICLES_LIST_VISIBLE, StartSurfaceConfiguration::updateFeedVisibility); 120 } 121 updateFeedVisibility()122 private static void updateFeedVisibility() { 123 SharedPreferencesManager.getInstance().writeBoolean( 124 ChromePreferenceKeys.FEED_ARTICLES_LIST_VISIBLE, 125 UserPrefs.get(Profile.getLastUsedRegularProfile()) 126 .getBoolean(Pref.ARTICLES_LIST_VISIBLE)); 127 } 128 129 /** 130 * @return Whether the Feed articles are visible. 131 */ getFeedArticlesVisibility()132 public static boolean getFeedArticlesVisibility() { 133 return SharedPreferencesManager.getInstance().readBoolean( 134 ChromePreferenceKeys.FEED_ARTICLES_LIST_VISIBLE, true); 135 } 136 137 @VisibleForTesting setFeedVisibilityForTesting(boolean isVisible)138 static void setFeedVisibilityForTesting(boolean isVisible) { 139 SharedPreferencesManager.getInstance().writeBoolean( 140 ChromePreferenceKeys.FEED_ARTICLES_LIST_VISIBLE, isVisible); 141 } 142 143 /** 144 * Records histograms of showing the StartSurface. Nothing will be recorded if timeDurationMs 145 * isn't valid. 146 */ recordHistogram(String name, long timeDurationMs, boolean isInstantStart)147 public static void recordHistogram(String name, long timeDurationMs, boolean isInstantStart) { 148 if (timeDurationMs < 0) return; 149 Log.i(TAG, "Recorded %s = %d ms", getHistogramName(name, isInstantStart), timeDurationMs); 150 RecordHistogram.recordTimesHistogram( 151 getHistogramName(name, isInstantStart), timeDurationMs); 152 } 153 154 @VisibleForTesting getHistogramName(String name, boolean isInstantStart)155 public static String getHistogramName(String name, boolean isInstantStart) { 156 return STARTUP_UMA_PREFIX + name 157 + (isInstantStart ? INSTANT_START_SUBFIX : REGULAR_START_SUBFIX); 158 } 159 /** 160 * @param isDense Whether the placeholder of Feed is dense. This depends on whether the first 161 * article card of Feed is dense. 162 */ setFeedPlaceholderDense(boolean isDense)163 public static void setFeedPlaceholderDense(boolean isDense) { 164 SharedPreferencesManager.getInstance().writeBoolean( 165 ChromePreferenceKeys.FEED_PLACEHOLDER_DENSE, isDense); 166 } 167 168 /** 169 * @return Whether the placeholder of Feed is dense. 170 */ isFeedPlaceholderDense()171 public static boolean isFeedPlaceholderDense() { 172 return SharedPreferencesManager.getInstance().readBoolean( 173 ChromePreferenceKeys.FEED_PLACEHOLDER_DENSE, false); 174 } 175 } 176