Index: chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2fe4333b8b9175a5498a7216b556677b1d447828 |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java |
@@ -0,0 +1,602 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import android.app.Activity; |
+import android.content.Context; |
+import android.content.SharedPreferences; |
+import android.os.Handler; |
+import android.preference.PreferenceManager; |
+import android.provider.Settings; |
+import android.util.Log; |
+import android.view.View; |
+ |
+import com.google.ipc.invalidation.external.client.android.service.AndroidLogger; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.base.ApplicationState; |
+import org.chromium.base.ApplicationStatus; |
+import org.chromium.base.ApplicationStatus.ApplicationStateListener; |
+import org.chromium.base.PathUtils; |
+import org.chromium.base.ResourceExtractor; |
+import org.chromium.base.ThreadUtils; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.annotations.SuppressFBWarnings; |
+import org.chromium.chrome.browser.accessibility.FontSizePrefs; |
+import org.chromium.chrome.browser.banners.AppBannerManager; |
+import org.chromium.chrome.browser.banners.AppDetailsDelegate; |
+import org.chromium.chrome.browser.document.ChromeLauncherActivity; |
+import org.chromium.chrome.browser.document.DocumentActivity; |
+import org.chromium.chrome.browser.document.IncognitoDocumentActivity; |
+import org.chromium.chrome.browser.document.TabDelegateImpl; |
+import org.chromium.chrome.browser.download.DownloadManagerService; |
+import org.chromium.chrome.browser.firstrun.FirstRunActivityStaging; |
+import org.chromium.chrome.browser.gsa.GSAHelper; |
+import org.chromium.chrome.browser.help.HelpAndFeedback; |
+import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory; |
+import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator; |
+import org.chromium.chrome.browser.invalidation.UniqueIdInvalidationClientNameGenerator; |
+import org.chromium.chrome.browser.metrics.UmaUtils; |
+import org.chromium.chrome.browser.metrics.VariationsSession; |
+import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; |
+import org.chromium.chrome.browser.omaha.RequestGenerator; |
+import org.chromium.chrome.browser.omaha.UpdateInfoBarHelper; |
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations; |
+import org.chromium.chrome.browser.policy.PolicyAuditor; |
+import org.chromium.chrome.browser.policy.PolicyManager; |
+import org.chromium.chrome.browser.policy.PolicyManager.PolicyChangeListener; |
+import org.chromium.chrome.browser.policy.providers.AppRestrictionsProvider; |
+import org.chromium.chrome.browser.preferences.AccessibilityPreferences; |
+import org.chromium.chrome.browser.preferences.PrefServiceBridge; |
+import org.chromium.chrome.browser.preferences.PreferencesStaging; |
+import org.chromium.chrome.browser.printing.PrintingControllerFactory; |
+import org.chromium.chrome.browser.rlz.RevenueStats; |
+import org.chromium.chrome.browser.services.GoogleServicesManager; |
+import org.chromium.chrome.browser.share.ShareHelper; |
+import org.chromium.chrome.browser.smartcard.EmptyPKCS11AuthenticationManager; |
+import org.chromium.chrome.browser.smartcard.PKCS11AuthenticationManager; |
+import org.chromium.chrome.browser.sync.SyncController; |
+import org.chromium.chrome.browser.tab.AuthenticatorNavigationInterceptor; |
+import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; |
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; |
+import org.chromium.content.browser.ChildProcessLauncher; |
+import org.chromium.content.browser.ContentViewStatics; |
+import org.chromium.content.browser.DownloadController; |
+import org.chromium.printing.PrintingController; |
+import org.chromium.ui.UiUtils; |
+ |
+import java.util.Locale; |
+ |
+/** |
+ * Per-process class that manages classes and functions shared by all of Chrome's Activities. |
+ */ |
+public class ChromeMobileApplication extends ChromiumApplication { |
+ private static final String PREF_LOCALE = "locale"; |
+ private static final float FLOAT_EPSILON = 0.001f; |
+ |
+ private static DocumentTabModelSelector sDocumentTabModelSelector; |
+ |
+ /** |
+ * This class allows pausing scripts & network connections when we |
+ * go to the background and resume when we are back in foreground again. |
+ * TODO(pliard): Get rid of this class once JavaScript timers toggling is done directly on |
+ * the native side by subscribing to the system monitor events. |
+ */ |
+ private static class BackgroundProcessing { |
+ private class SuspendRunnable implements Runnable { |
+ @Override |
+ public void run() { |
+ mSuspendRunnable = null; |
+ assert !mWebKitTimersAreSuspended; |
+ mWebKitTimersAreSuspended = true; |
+ ContentViewStatics.setWebKitSharedTimersSuspended(true); |
+ } |
+ } |
+ |
+ private static final int SUSPEND_TIMERS_AFTER_MS = 5 * 60 * 1000; |
+ private final Handler mHandler = new Handler(); |
+ private boolean mWebKitTimersAreSuspended = false; |
+ private SuspendRunnable mSuspendRunnable; |
+ |
+ private void onDestroy() { |
+ if (mSuspendRunnable != null) { |
+ mHandler.removeCallbacks(mSuspendRunnable); |
+ mSuspendRunnable = null; |
+ } |
+ } |
+ |
+ private void suspendTimers() { |
+ if (mSuspendRunnable == null) { |
+ mSuspendRunnable = new SuspendRunnable(); |
+ mHandler.postDelayed(mSuspendRunnable, SUSPEND_TIMERS_AFTER_MS); |
+ } |
+ } |
+ |
+ private void startTimers() { |
+ if (mSuspendRunnable != null) { |
+ mHandler.removeCallbacks(mSuspendRunnable); |
+ mSuspendRunnable = null; |
+ } else if (mWebKitTimersAreSuspended) { |
+ ContentViewStatics.setWebKitSharedTimersSuspended(false); |
+ mWebKitTimersAreSuspended = false; |
+ } |
+ } |
+ } |
+ |
+ private static final String[] CHROME_MANDATORY_PAKS = { |
+ "en-US.pak", "resources.pak", "chrome_100_percent.pak", "icudtl.dat", |
+ "natives_blob.bin", "snapshot_blob.bin" |
+ }; |
+ private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome"; |
+ private static final String DEV_TOOLS_SERVER_SOCKET_PREFIX = "chrome"; |
+ private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions.id"; |
+ |
+ private final BackgroundProcessing mBackgroundProcessing = new BackgroundProcessing(); |
+ private final PowerBroadcastReceiver mPowerBroadcastReceiver = new PowerBroadcastReceiver(); |
+ private final UpdateInfoBarHelper mUpdateInfoBarHelper = new UpdateInfoBarHelper(); |
+ |
+ // Used to trigger variation changes (such as seed fetches) upon application foregrounding. |
+ private VariationsSession mVariationsSession; |
+ |
+ private DevToolsServer mDevToolsServer; |
+ |
+ private boolean mIsStarted; |
+ private boolean mInitializedSharedClasses; |
+ private boolean mIsProcessInitialized; |
+ |
+ private ChromeLifetimeController mChromeLifetimeController; |
+ private PrintingController mPrintingController; |
+ private PolicyManager mPolicyManager; |
+ |
+ /** |
+ * This is called once per ChromeMobileApplication instance, which get created per process |
+ * (browser OR renderer). Don't stick anything in here that shouldn't be called multiple times |
+ * during Chrome's lifetime. |
+ */ |
+ @Override |
+ public void onCreate() { |
+ UmaUtils.recordMainEntryPointTime(); |
+ super.onCreate(); |
+ UiUtils.setKeyboardShowingDelegate(new UiUtils.KeyboardShowingDelegate() { |
+ @Override |
+ public boolean disableKeyboardCheck(Context context, View view) { |
+ Activity activity = null; |
+ if (context instanceof Activity) { |
+ activity = (Activity) context; |
+ } else if (view != null && view.getContext() instanceof Activity) { |
+ activity = (Activity) view.getContext(); |
+ } |
+ |
+ // For multiwindow mode we do not track keyboard visibility. |
+ return activity != null && isMultiWindow(activity); |
+ } |
+ }); |
+ |
+ // Set the unique identification generator for invalidations. The |
+ // invalidations system can start and attempt to fetch the client ID |
+ // very early. We need this generator to be ready before that happens. |
+ UniqueIdInvalidationClientNameGenerator.doInitializeAndInstallGenerator(this); |
+ |
+ // Set minimum Tango log level. This sets an in-memory static field, and needs to be |
+ // set in the ApplicationContext instead of an activity, since Tango can be woken up |
+ // by the system directly though messages from GCM. |
+ AndroidLogger.setMinimumAndroidLogLevel(Log.WARN); |
+ |
+ // Set up the identification generator for sync. The ID is actually generated |
+ // in the SyncController constructor. |
+ UniqueIdentificationGeneratorFactory.registerGenerator(SyncController.GENERATOR_ID, |
+ new UuidBasedUniqueIdentificationGenerator(this, SESSIONS_UUID_PREF_KEY), false); |
+ } |
+ |
+ @Override |
+ protected void initializeLibraryDependencies() { |
+ // The ResourceExtractor is only needed by the browser process, but this will have no |
+ // impact on the renderer process construction. |
+ ResourceExtractor.setMandatoryPaksToExtract(CHROME_MANDATORY_PAKS); |
+ PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, this); |
+ } |
+ |
+ @Override |
+ protected void initializeGoogleServicesManager() { |
+ GoogleServicesManager.get(getApplicationContext()); |
+ } |
+ |
+ @Override |
+ public void initCommandLine() { |
+ ChromeCommandLineInitUtil.initChromeCommandLine(this); |
+ } |
+ |
+ /** |
+ * The host activity should call this after the native library has loaded to ensure classes |
+ * shared by Activities in the same process are properly initialized. |
+ */ |
+ public void initializeSharedClasses() { |
+ if (mInitializedSharedClasses) return; |
+ mInitializedSharedClasses = true; |
+ |
+ GoogleServicesManager.get(this).onMainActivityStart(); |
+ RevenueStats.getInstance(); |
+ ShortcutHelper.setFullScreenAction(ChromeLauncherActivity.ACTION_START_WEBAPP); |
+ |
+ getPKCS11AuthenticationManager().initialize(ChromeMobileApplication.this); |
+ |
+ mDevToolsServer = new DevToolsServer(DEV_TOOLS_SERVER_SOCKET_PREFIX); |
+ mDevToolsServer.setRemoteDebuggingEnabled( |
+ true, DevToolsServer.Security.ALLOW_DEBUG_PERMISSION); |
+ |
+ startApplicationActivityTracker(); |
+ |
+ DownloadController.setDownloadNotificationService( |
+ DownloadManagerService.getDownloadManagerService(this)); |
+ |
+ if (ApiCompatibilityUtils.isPrintingSupported()) { |
+ mPrintingController = PrintingControllerFactory.create(getApplicationContext()); |
+ } |
+ } |
+ |
+ /** |
+ * @return The Application's PowerBroadcastReceiver. |
+ */ |
+ @VisibleForTesting |
+ public PowerBroadcastReceiver getPowerBroadcastReceiver() { |
+ return mPowerBroadcastReceiver; |
+ } |
+ |
+ /** |
+ * Update the font size after changing the Android accessibility system setting. Doing so kills |
+ * the Activities but it doesn't kill the ChromeMobileApplication, so this should be called in |
+ * {@link #onStart} instead of {@link #initialize}. |
+ */ |
+ private void updateFontSize() { |
+ // This method is currently broken. http://crbug.com/439108 |
+ // Skip it (with the consequence of not updating the text scaling factor when the user |
+ // changes system font size) rather than incurring the broken behavior. |
+ // TODO(newt): fix this. |
+ if (true) return; |
+ |
+ FontSizePrefs fontSizePrefs = FontSizePrefs.getInstance(getApplicationContext()); |
+ |
+ // Set font scale factor as the product of the system and browser scale settings. |
+ float browserTextScale = PreferenceManager |
+ .getDefaultSharedPreferences(this) |
+ .getFloat(AccessibilityPreferences.PREF_TEXT_SCALE, 1.0f); |
+ float fontScale = getResources().getConfiguration().fontScale * browserTextScale; |
+ |
+ float scaleDelta = Math.abs(fontScale - fontSizePrefs.getFontScaleFactor()); |
+ if (scaleDelta >= FLOAT_EPSILON) { |
+ fontSizePrefs.setFontScaleFactor(fontScale); |
+ } |
+ |
+ // If force enable zoom has not been manually set, set it automatically based on |
+ // font scale factor. |
+ boolean shouldForceZoom = |
+ fontScale >= AccessibilityPreferences.FORCE_ENABLE_ZOOM_THRESHOLD_MULTIPLIER; |
+ if (!fontSizePrefs.getUserSetForceEnableZoom() |
+ && fontSizePrefs.getForceEnableZoom() != shouldForceZoom) { |
+ fontSizePrefs.setForceEnableZoom(shouldForceZoom); |
+ } |
+ } |
+ |
+ /** |
+ * Update the accept languages after changing Android locale setting. Doing so kills the |
+ * Activities but it doesn't kill the ChromeMobileApplication, so this should be called in |
+ * {@link #onStart} instead of {@link #initialize}. |
+ */ |
+ private void updateAcceptLanguages() { |
+ PrefServiceBridge instance = PrefServiceBridge.getInstance(); |
+ String localeString = Locale.getDefault().toString(); // ex) en_US, de_DE, zh_CN_#Hans |
+ if (hasLocaleChanged(localeString)) { |
+ instance.resetAcceptLanguages(localeString); |
+ // Clear cache so that accept-languages change can be applied immediately. |
+ // TODO(changwan): The underlying BrowsingDataRemover::Remove() is an asynchronous call. |
+ // So cache-clearing may not be effective if URL rendering can happen before |
+ // OnBrowsingDataRemoverDone() is called, in which case we may have to reload as well. |
+ // Check if it can happen. |
+ instance.clearBrowsingData(null, false, true /* cache */, false, false, false); |
+ } |
+ } |
+ |
+ private boolean hasLocaleChanged(String newLocale) { |
+ String previousLocale = PreferenceManager.getDefaultSharedPreferences(this).getString( |
+ PREF_LOCALE, ""); |
+ |
+ if (!previousLocale.equals(newLocale)) { |
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
+ SharedPreferences.Editor editor = prefs.edit(); |
+ editor.putString(PREF_LOCALE, newLocale); |
+ editor.apply(); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Should be called almost immediately after the native library has loaded to initialize things |
+ * that really, really have to be set up early. Avoid putting any long tasks here. |
+ */ |
+ @Override |
+ public void initializeProcess() { |
+ if (mIsProcessInitialized) return; |
+ mIsProcessInitialized = true; |
+ assert !mIsStarted; |
+ |
+ super.initializeProcess(); |
+ |
+ mVariationsSession = createVariationsSession(); |
+ removeSessionCookies(); |
+ ApplicationStatus.registerApplicationStateListener(createApplicationStateListener()); |
+ AppBannerManager.setAppDetailsDelegate(createAppDetailsDelegate()); |
+ mChromeLifetimeController = new ChromeLifetimeController(this); |
+ mPolicyManager = new PolicyManager(); |
+ registerPolicyProviders(mPolicyManager); |
+ |
+ PrefServiceBridge.getInstance().migratePreferences(this); |
+ } |
+ |
+ /** |
+ * Each top-level activity (ChromeTabbedActivity, FullscreenActivity) should call this during |
+ * its onStart phase. When called for the first time, this marks the beginning of a foreground |
+ * session and calls onForegroundSessionStart(). Subsequent calls are noops until |
+ * onForegroundSessionEnd() is called, to handle changing top-level Chrome activities in one |
+ * foreground session. |
+ */ |
+ public void onStartWithNative() { |
+ if (mIsStarted) return; |
+ mIsStarted = true; |
+ |
+ assert mIsProcessInitialized; |
+ |
+ onForegroundSessionStart(); |
+ } |
+ |
+ /** |
+ * Called when a top-level Chrome activity (ChromeTabbedActivity, FullscreenActivity) is |
+ * started in foreground. It will not be called again when other Chrome activities take over |
+ * (see onStart()), that is, when correct activity calls startActivity() for another Chrome |
+ * activity. |
+ */ |
+ private void onForegroundSessionStart() { |
+ ChildProcessLauncher.onBroughtToForeground(); |
+ mBackgroundProcessing.startTimers(); |
+ updatePasswordEchoState(); |
+ updateFontSize(); |
+ updateAcceptLanguages(); |
+ changeAppStatus(true); |
+ mVariationsSession.start(getApplicationContext()); |
+ |
+ mPowerBroadcastReceiver.registerReceiver(this); |
+ mPowerBroadcastReceiver.runActions(this, true); |
+ } |
+ |
+ /** |
+ * Called when last of Chrome activities is stopped, ending the foreground session. This will |
+ * not be called when a Chrome activity is stopped because another Chrome activity takes over. |
+ * This is ensured by ActivityStatus, which switches to track new activity when its started and |
+ * will not report the old one being stopped (see createStateListener() below). |
+ */ |
+ private void onForegroundSessionEnd() { |
+ if (!mIsStarted) return; |
+ mBackgroundProcessing.suspendTimers(); |
+ flushPersistentData(); |
+ mIsStarted = false; |
+ changeAppStatus(false); |
+ |
+ try { |
+ mPowerBroadcastReceiver.unregisterReceiver(this); |
+ } catch (IllegalArgumentException e) { |
+ // This may happen when onStop get called very early in UI test. |
+ } |
+ |
+ ChildProcessLauncher.onSentToBackground(); |
+ } |
+ |
+ /** |
+ * Called after onForegroundSessionEnd() indicating that the activity whose onStop() ended the |
+ * last foreground session was destroyed. |
+ */ |
+ private void onForegroundActivityDestroyed() { |
+ if (ApplicationStatus.isEveryActivityDestroyed()) { |
+ mBackgroundProcessing.onDestroy(); |
+ stopApplicationActivityTracker(); |
+ PartnerBrowserCustomizations.destroy(); |
+ ShareHelper.clearSharedScreenshots(this); |
+ mPolicyManager.destroy(); |
+ mPolicyManager = null; |
+ } |
+ } |
+ |
+ private ApplicationStateListener createApplicationStateListener() { |
+ return new ApplicationStateListener() { |
+ @Override |
+ public void onApplicationStateChange(int newState) { |
+ if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) { |
+ onForegroundSessionEnd(); |
+ } else if (newState == ApplicationState.HAS_DESTROYED_ACTIVITIES) { |
+ onForegroundActivityDestroyed(); |
+ } |
+ } |
+ }; |
+ } |
+ |
+ /** |
+ * Returns a new instance of HelpAndFeedback. |
+ */ |
+ public HelpAndFeedback createHelpAndFeedback() { |
+ return new HelpAndFeedback(); |
+ } |
+ |
+ /** |
+ * Returns a new instance of VariationsSession. |
+ */ |
+ public VariationsSession createVariationsSession() { |
+ return new VariationsSession(); |
+ } |
+ |
+ /** |
+ * Return a {@link AuthenticatorNavigationInterceptor} for the given {@link Tab}. |
+ * This can be null if there are no applicable interceptor to be built. |
+ */ |
+ @SuppressWarnings("unused") |
+ public AuthenticatorNavigationInterceptor createAuthenticatorNavigationInterceptor(Tab tab) { |
+ return null; |
+ } |
+ |
+ /** |
+ * Starts the application activity tracker. |
+ */ |
+ protected void startApplicationActivityTracker() {} |
+ |
+ /** |
+ * Stops the application activity tracker. |
+ */ |
+ protected void stopApplicationActivityTracker() {} |
+ |
+ // TODO(newt): delete this method after upstreaming. Callers can use |
+ // MultiWindowUtils.getInstance() instead. |
+ @Override |
+ public boolean isMultiWindow(Activity activity) { |
+ return MultiWindowUtils.getInstance().isMultiWindow(activity); |
+ } |
+ |
+ // TODO(newt): delete this after upstreaming. |
+ @Override |
+ public String getSettingsActivityName() { |
+ return PreferencesStaging.class.getName(); |
+ } |
+ |
+ // TODO(aurimas): delete this after upstreaming. |
+ @Override |
+ public String getFirstRunActivityName() { |
+ return FirstRunActivityStaging.class.getName(); |
+ } |
+ |
+ /** |
+ * Honor the Android system setting about showing the last character of a password for a short |
+ * period of time. |
+ */ |
+ private void updatePasswordEchoState() { |
+ boolean systemEnabled = Settings.System.getInt( |
+ getApplicationContext().getContentResolver(), |
+ Settings.System.TEXT_SHOW_PASSWORD, 1) == 1; |
+ if (PrefServiceBridge.getInstance().getPasswordEchoEnabled() == systemEnabled) return; |
+ |
+ PrefServiceBridge.getInstance().setPasswordEchoEnabled(systemEnabled); |
+ } |
+ |
+ @Override |
+ protected PKCS11AuthenticationManager getPKCS11AuthenticationManager() { |
+ return EmptyPKCS11AuthenticationManager.getInstance(); |
+ } |
+ |
+ /** |
+ * @return Instance of printing controller that is shared among all chromium activities. May |
+ * return null if printing is not supported on the platform. |
+ */ |
+ public PrintingController getPrintingController() { |
+ return mPrintingController; |
+ } |
+ |
+ /** |
+ * @return The UpdateInfoBarHelper used to inform the user about updates. |
+ */ |
+ public UpdateInfoBarHelper getUpdateInfoBarHelper() { |
+ return mUpdateInfoBarHelper; |
+ } |
+ |
+ /** |
+ * @return An instance of {@link GSAHelper} that handles the start point of chrome's integration |
+ * with GSA. |
+ */ |
+ public GSAHelper createGsaHelper() { |
+ return new GSAHelper(); |
+ } |
+ |
+ @VisibleForTesting |
+ public PolicyManager getPolicyManagerForTesting() { |
+ return mPolicyManager; |
+ } |
+ |
+ /** |
+ * Registers various policy providers with the policy manager. |
+ * Providers are registered in increasing order of precedence so overrides should call this |
+ * method in the end for this method to maintain the highest precedence. |
+ * @param manager The {@link PolicyManager} to register the providers with. |
+ */ |
+ protected void registerPolicyProviders(PolicyManager manager) { |
+ manager.registerProvider(new AppRestrictionsProvider(getApplicationContext())); |
+ } |
+ |
+ public void addPolicyChangeListener(PolicyChangeListener listener) { |
+ mPolicyManager.addPolicyChangeListener(listener); |
+ } |
+ |
+ public void removePolicyChangeListener(PolicyChangeListener listener) { |
+ mPolicyManager.removePolicyChangeListener(listener); |
+ } |
+ |
+ /** |
+ * @return An instance of PolicyAuditor that notifies the policy system of the user's activity. |
+ * Only applicable when the user has a policy active, that is tracking the activity. |
+ */ |
+ public PolicyAuditor getPolicyAuditor() { |
+ // This class has a protected constructor to prevent accidental instantiation. |
+ return new PolicyAuditor() {}; |
+ } |
+ |
+ /** |
+ * @return An instance of MultiWindowUtils to be installed as a singleton. |
+ */ |
+ public MultiWindowUtils createMultiWindowUtils() { |
+ return new MultiWindowUtils(); |
+ } |
+ |
+ /** |
+ * @return An instance of RequestGenerator to be used for Omaha XML creation. Will be null if |
+ * a generator is unavailable. |
+ */ |
+ public RequestGenerator createOmahaRequestGenerator() { |
+ return null; |
+ } |
+ |
+ /** |
+ * @return An instance of AppDetailsDelegate that can be queried about app information for the |
+ * App Banner feature. Will be null if one is unavailable. |
+ */ |
+ protected AppDetailsDelegate createAppDetailsDelegate() { |
+ return null; |
+ } |
+ |
+ /** |
+ * Returns the Singleton instance of the DocumentTabModelSelector. |
+ * TODO(dfalcantara): Find a better place for this once we differentiate between activity and |
+ * application-level TabModelSelectors. |
+ * @return The DocumentTabModelSelector for the application. |
+ */ |
+ @SuppressFBWarnings("LI_LAZY_INIT_STATIC") |
+ public static DocumentTabModelSelector getDocumentTabModelSelector() { |
+ ThreadUtils.assertOnUiThread(); |
+ if (sDocumentTabModelSelector == null) { |
+ sDocumentTabModelSelector = new DocumentTabModelSelector( |
+ new ActivityDelegate(DocumentActivity.class, IncognitoDocumentActivity.class), |
+ new TabDelegateImpl()); |
+ } |
+ return sDocumentTabModelSelector; |
+ } |
+ |
+ /** |
+ * @return Whether or not the Singleton has been initialized. |
+ */ |
+ @VisibleForTesting |
+ public static boolean isDocumentTabModelSelectorInitializedForTests() { |
+ return sDocumentTabModelSelector != null; |
+ } |
+ |
+ /** |
+ * @return An instance of RevenueStats to be installed as a singleton. |
+ */ |
+ public RevenueStats createRevenueStatsInstance() { |
+ return new RevenueStats(); |
+ } |
+} |