OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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.browser.gsa; |
| 6 |
| 7 import android.text.TextUtils; |
| 8 |
| 9 import org.chromium.base.Log; |
| 10 import org.chromium.base.metrics.RecordHistogram; |
| 11 import org.chromium.chrome.browser.ChromeActivity; |
| 12 import org.chromium.chrome.browser.Tab; |
| 13 import org.chromium.chrome.browser.UrlConstants; |
| 14 import org.chromium.chrome.browser.contextualsearch.ContextualSearchObserver; |
| 15 import org.chromium.chrome.browser.sync.ProfileSyncService; |
| 16 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
| 17 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 18 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
| 19 import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| 20 import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| 21 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; |
| 22 import org.chromium.sync.internal_api.pub.PassphraseType; |
| 23 import org.chromium.sync.internal_api.pub.base.ModelType; |
| 24 |
| 25 import java.util.concurrent.atomic.AtomicBoolean; |
| 26 |
| 27 import javax.annotation.Nullable; |
| 28 |
| 29 /** |
| 30 * Reports context to GSA for search quality. |
| 31 */ |
| 32 public class ContextReporter { |
| 33 private static final String TAG = Log.makeTag("GSA"); |
| 34 |
| 35 // Values for UMA histogram. |
| 36 public static final int STATUS_SUCCESS = 0; |
| 37 public static final int STATUS_GSA_NOT_AVAILABLE = 1; |
| 38 public static final int STATUS_SYNC_NOT_INITIALIZED = 2; |
| 39 public static final int STATUS_SYNC_NOT_SYNCING_URLS = 3; |
| 40 public static final int STATUS_SYNC_NOT_KEYSTORE_PASSPHRASE = 4; |
| 41 public static final int STATUS_SYNC_OTHER = 5; |
| 42 public static final int STATUS_SVELTE_DEVICE = 6; |
| 43 public static final int STATUS_NO_TAB = 7; |
| 44 public static final int STATUS_INCOGNITO = 8; |
| 45 public static final int STATUS_INVALID_SCHEME = 9; |
| 46 public static final int STATUS_TAB_ID_MISMATCH = 10; |
| 47 public static final int STATUS_DUP_TITLE_CHANGE = 11; |
| 48 public static final int STATUS_CONNECTION_FAILED = 12; |
| 49 public static final int STATUS_SYNC_NOT_READY_AT_REPORT_TIME = 13; |
| 50 public static final int STATUS_NOT_SIGNED_IN = 14; |
| 51 public static final int STATUS_GSA_ACCOUNT_MISSING = 15; |
| 52 public static final int STATUS_GSA_ACCOUNT_MISMATCH = 16; |
| 53 public static final int STATUS_RESULT_IS_NULL = 17; |
| 54 public static final int STATUS_RESULT_FAILED = 18; |
| 55 public static final int STATUS_SUCCESS_WITH_SELECTION = 19; |
| 56 // This should always stay last and have the highest number. |
| 57 private static final int STATUS_BOUNDARY = 20; |
| 58 |
| 59 private final ChromeActivity mActivity; |
| 60 private final GSAContextReportDelegate mDelegate; |
| 61 private TabModelSelectorTabObserver mSelectorTabObserver; |
| 62 private TabModelObserver mModelObserver; |
| 63 private ContextualSearchObserver mContextualSearchObserver; |
| 64 private boolean mLastContextWasTitleChange; |
| 65 private final AtomicBoolean mContextInUse; |
| 66 |
| 67 /** |
| 68 * Creates a ContextReporter for an Activity. |
| 69 * @param activity Chrome Activity which context will be reported. |
| 70 * @param controller used to communicate with GSA |
| 71 */ |
| 72 public ContextReporter(ChromeActivity activity, GSAContextReportDelegate con
troller) { |
| 73 mActivity = activity; |
| 74 mDelegate = controller; |
| 75 mContextInUse = new AtomicBoolean(false); |
| 76 Log.d(TAG, "Created a new ContextReporter"); |
| 77 } |
| 78 |
| 79 /** |
| 80 * Starts reporting context. |
| 81 */ |
| 82 public void enable() { |
| 83 Tab currentTab = mActivity.getActivityTab(); |
| 84 reportUsageOfCurrentContextIfPossible(currentTab, false, null); |
| 85 |
| 86 TabModelSelector selector = mActivity.getTabModelSelector(); |
| 87 assert selector != null; |
| 88 |
| 89 if (mSelectorTabObserver == null) { |
| 90 mSelectorTabObserver = new TabModelSelectorTabObserver(selector) { |
| 91 @Override |
| 92 public void onTitleUpdated(Tab tab) { |
| 93 // Report usage declaring this as a title change. |
| 94 reportUsageOfCurrentContextIfPossible(tab, true, null); |
| 95 } |
| 96 |
| 97 @Override |
| 98 public void onUrlUpdated(Tab tab) { |
| 99 reportUsageOfCurrentContextIfPossible(tab, false, null); |
| 100 } |
| 101 }; |
| 102 } |
| 103 if (mModelObserver == null) { |
| 104 assert !selector.getModels().isEmpty(); |
| 105 mModelObserver = new EmptyTabModelObserver() { |
| 106 @Override |
| 107 public void didSelectTab(Tab tab, TabSelectionType type, int las
tId) { |
| 108 reportUsageOfCurrentContextIfPossible(tab, false, null); |
| 109 } |
| 110 }; |
| 111 for (TabModel model : selector.getModels()) { |
| 112 model.addObserver(mModelObserver); |
| 113 } |
| 114 } |
| 115 if (mContextualSearchObserver == null && mActivity.getContextualSearchMa
nager() != null) { |
| 116 mContextualSearchObserver = new ContextualSearchObserver() { |
| 117 @Override |
| 118 public void onShowContextualSearch(GSAContextDisplaySelection co
ntextSelection) { |
| 119 if (contextSelection != null) reportDisplaySelection(context
Selection); |
| 120 } |
| 121 |
| 122 @Override |
| 123 public void onHideContextualSearch() { |
| 124 reportDisplaySelection(null); |
| 125 } |
| 126 }; |
| 127 mActivity.getContextualSearchManager().addObserver(mContextualSearch
Observer); |
| 128 } |
| 129 } |
| 130 |
| 131 /** |
| 132 * Stops reporting context. Called when the app goes to the background. |
| 133 */ |
| 134 public void disable() { |
| 135 reportUsageEndedIfNecessary(); |
| 136 |
| 137 if (mSelectorTabObserver != null) { |
| 138 mSelectorTabObserver.destroy(); |
| 139 mSelectorTabObserver = null; |
| 140 } |
| 141 if (mModelObserver != null) { |
| 142 for (TabModel model : mActivity.getTabModelSelector().getModels()) { |
| 143 model.removeObserver(mModelObserver); |
| 144 } |
| 145 mModelObserver = null; |
| 146 } |
| 147 if (mContextualSearchObserver != null && mActivity.getContextualSearchMa
nager() != null) { |
| 148 mActivity.getContextualSearchManager().removeObserver(mContextualSea
rchObserver); |
| 149 mContextualSearchObserver = null; |
| 150 } |
| 151 } |
| 152 |
| 153 /** |
| 154 * Reports that the given display selection has been established for the cur
rent tab. |
| 155 * @param displaySelection The information about the selection being display
ed. |
| 156 */ |
| 157 private void reportDisplaySelection(@Nullable GSAContextDisplaySelection dis
playSelection) { |
| 158 Tab currentTab = mActivity.getActivityTab(); |
| 159 reportUsageOfCurrentContextIfPossible(currentTab, false, displaySelectio
n); |
| 160 } |
| 161 |
| 162 private void reportUsageEndedIfNecessary() { |
| 163 if (mContextInUse.compareAndSet(true, false)) mDelegate.reportContextUsa
geEnded(); |
| 164 } |
| 165 |
| 166 private void reportUsageOfCurrentContextIfPossible( |
| 167 Tab tab, boolean isTitleChange, @Nullable GSAContextDisplaySelection
displaySelection) { |
| 168 Tab currentTab = mActivity.getActivityTab(); |
| 169 if (currentTab == null || currentTab.isIncognito()) { |
| 170 if (currentTab == null) { |
| 171 reportStatus(STATUS_NO_TAB); |
| 172 Log.d(TAG, "Not reporting, tab is null"); |
| 173 } else { |
| 174 reportStatus(STATUS_INCOGNITO); |
| 175 Log.d(TAG, "Not reporting, tab is incognito"); |
| 176 } |
| 177 reportUsageEndedIfNecessary(); |
| 178 return; |
| 179 } |
| 180 |
| 181 String currentUrl = currentTab.getUrl(); |
| 182 if (TextUtils.isEmpty(currentUrl) || !(currentUrl.startsWith(UrlConstant
s.HTTP_SCHEME) |
| 183 || currentUrl.startsWith(UrlConstants.HTTPS_SCHEME))) { |
| 184 reportStatus(STATUS_INVALID_SCHEME); |
| 185 Log.d(TAG, "Not reporting, URL scheme is invalid"); |
| 186 reportUsageEndedIfNecessary(); |
| 187 return; |
| 188 } |
| 189 |
| 190 // Check whether this is a context change we would like to report. |
| 191 if (currentTab.getId() != tab.getId()) { |
| 192 reportStatus(STATUS_TAB_ID_MISMATCH); |
| 193 Log.d(TAG, "Not reporting, tab ID doesn't match"); |
| 194 return; |
| 195 } |
| 196 if (isTitleChange && mLastContextWasTitleChange) { |
| 197 reportStatus(STATUS_DUP_TITLE_CHANGE); |
| 198 Log.d(TAG, "Not reporting, repeated title update"); |
| 199 return; |
| 200 } |
| 201 |
| 202 reportUsageEndedIfNecessary(); |
| 203 |
| 204 mDelegate.reportContext(currentTab.getUrl(), currentTab.getTitle(), disp
laySelection); |
| 205 mLastContextWasTitleChange = isTitleChange; |
| 206 mContextInUse.set(true); |
| 207 } |
| 208 |
| 209 /** |
| 210 * Records the given status via UMA. |
| 211 * Use one of the STATUS_* constants above. |
| 212 */ |
| 213 public static void reportStatus(int status) { |
| 214 RecordHistogram.recordEnumeratedHistogram( |
| 215 "Search.IcingContextReportingStatus", status, STATUS_BOUNDARY); |
| 216 } |
| 217 |
| 218 /** |
| 219 * Records an appropriate status via UMA given the current sync status. |
| 220 */ |
| 221 public static void reportSyncStatus(ProfileSyncService syncService) { |
| 222 if (!syncService.isSyncInitialized()) { |
| 223 reportStatus(STATUS_SYNC_NOT_INITIALIZED); |
| 224 } else if (!syncService.getActiveDataTypes().contains(ModelType.TYPED_UR
L)) { |
| 225 reportStatus(STATUS_SYNC_NOT_SYNCING_URLS); |
| 226 } else if (!syncService.getPassphraseType().equals(PassphraseType.KEYSTO
RE_PASSPHRASE)) { |
| 227 reportStatus(STATUS_SYNC_NOT_KEYSTORE_PASSPHRASE); |
| 228 } else { |
| 229 reportStatus(STATUS_SYNC_OTHER); |
| 230 } |
| 231 } |
| 232 } |
OLD | NEW |