// Copyright 2016 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.graphics.Bitmap; import android.text.TextUtils; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.chrome.browser.favicon.FaviconHelper; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; import org.chromium.chrome.browser.tabmodel.TabModelObserver; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; import org.chromium.chrome.browser.util.UrlUtilities; import org.chromium.components.security_state.ConnectionSecurityLevel; import java.util.List; /** * Helper that updates the Android task description given the state of the current tab. * * <p> * The task description is what is shown in Android's Overview/Recents screen for each entry. */ public class ActivityTabTaskDescriptionHelper { private final int mDefaultThemeColor; private final ChromeActivity mActivity; private final TabModelSelector mTabModelSelector; private final ActivityTaskDescriptionIconGenerator mIconGenerator; private final FaviconHelper mFaviconHelper; private final TabModelSelectorObserver mTabModelSelectorObserver; private final TabModelObserver mTabModelObserver; private final TabObserver mTabObserver; private Bitmap mLargestFavicon; private Tab mCurrentTab; /** * Constructs a task description helper for the given activity. * * @param activity The activity whose descriptions should be updated. * @param defaultThemeColor The default theme color to be used if the tab does not override it. */ public ActivityTabTaskDescriptionHelper(ChromeActivity activity, int defaultThemeColor) { mActivity = activity; mDefaultThemeColor = defaultThemeColor; mTabModelSelector = mActivity.getTabModelSelector(); mIconGenerator = new ActivityTaskDescriptionIconGenerator(activity); mFaviconHelper = new FaviconHelper(); mTabObserver = new EmptyTabObserver() { @Override public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) { if (!didStartLoad) return; resetIcon(); } @Override public void onFaviconUpdated(Tab tab, Bitmap icon) { if (icon == null) return; updateFavicon(icon); } @Override public void onUrlUpdated(Tab tab) { updateTaskDescription(); } @Override public void onTitleUpdated(Tab tab) { updateTaskDescription(); } @Override public void onSSLStateUpdated(Tab tab) { if (hasSecurityWarningOrError(tab)) resetIcon(); } @Override public void onDidNavigateMainFrame(Tab tab, String url, String baseUrl, boolean isNavigationToDifferentPage, boolean isFragmentNavigation, int statusCode) { if (!isNavigationToDifferentPage) return; mLargestFavicon = null; updateTaskDescription(); } @Override public void onLoadStopped(Tab tab, boolean toDifferentDocument) { updateTaskDescription(); } @Override public void onDidChangeThemeColor(Tab tab, int color) { updateTaskDescription(); } @Override public void onDidAttachInterstitialPage(Tab tab) { resetIcon(); } @Override public void onDidDetachInterstitialPage(Tab tab) { resetIcon(); } private boolean hasSecurityWarningOrError(Tab tab) { int securityLevel = tab.getSecurityLevel(); return securityLevel == ConnectionSecurityLevel.DANGEROUS || securityLevel == ConnectionSecurityLevel.SECURITY_WARNING || securityLevel == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT; } }; mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() { @Override public void onTabModelSelected(TabModel newModel, TabModel oldModel) { refreshSelectedTab(); } }; mTabModelObserver = new EmptyTabModelObserver() { @Override public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { refreshSelectedTab(); } @Override public void tabClosureUndone(Tab tab) { refreshSelectedTab(); } @Override public void didCloseTab(int tabId, boolean incognito) { refreshSelectedTab(); } @Override public void tabRemoved(Tab tab) { refreshSelectedTab(); } @Override public void tabPendingClosure(Tab tab) { refreshSelectedTab(); } @Override public void allTabsPendingClosure(List<Integer> tabIds) { refreshSelectedTab(); } }; mTabModelSelector.addObserver(mTabModelSelectorObserver); for (TabModel model : mTabModelSelector.getModels()) model.addObserver(mTabModelObserver); refreshSelectedTab(); } private void resetIcon() { mLargestFavicon = null; updateTaskDescription(); } private void updateFavicon(Bitmap favicon) { if (favicon == null) return; if (mLargestFavicon == null || favicon.getWidth() > mLargestFavicon.getWidth() || favicon.getHeight() > mLargestFavicon.getHeight()) { mLargestFavicon = favicon; updateTaskDescription(); } } private void updateTaskDescription() { if (mCurrentTab == null) { updateTaskDescription(null, null); return; } if (NewTabPage.isNTPUrl(mCurrentTab.getUrl()) && !mCurrentTab.isIncognito()) { // NTP needs a new color in recents, but uses the default application title and icon updateTaskDescription(null, null); return; } String label = mCurrentTab.getTitle(); String domain = UrlUtilities.getDomainAndRegistry(mCurrentTab.getUrl(), false); if (TextUtils.isEmpty(label)) { label = domain; } if (mLargestFavicon == null && TextUtils.isEmpty(label)) { updateTaskDescription(null, null); return; } Bitmap bitmap = null; if (!mCurrentTab.isIncognito()) { bitmap = mIconGenerator.getBitmap(mCurrentTab.getUrl(), mLargestFavicon); } updateTaskDescription(label, bitmap); } /** * Update the task description with the specified icon and label. * * <p> * This is only publicly visible to allow activities to set this early during initialization * prior to the tab's being available. * * @param label The text to use in the task description. * @param icon The icon to use in the task description. */ public void updateTaskDescription(String label, Bitmap icon) { int color = mDefaultThemeColor; if (mCurrentTab != null && !mCurrentTab.isDefaultThemeColor()) { color = mCurrentTab.getThemeColor(); } ApiCompatibilityUtils.setTaskDescription(mActivity, label, icon, color); } private void refreshSelectedTab() { Tab tab = mTabModelSelector.getCurrentTab(); if (mCurrentTab == tab) return; if (mCurrentTab != null) mCurrentTab.removeObserver(mTabObserver); mCurrentTab = tab; if (mCurrentTab != null) { mCurrentTab.addObserver(mTabObserver); final String currentUrl = mCurrentTab.getUrl(); mFaviconHelper.getLocalFaviconImageForURL( mCurrentTab.getProfile(), mCurrentTab.getUrl(), 0, new FaviconHelper.FaviconImageCallback() { @Override public void onFaviconAvailable(Bitmap image, String iconUrl) { if (mCurrentTab == null || !TextUtils.equals(currentUrl, mCurrentTab.getUrl())) { return; } updateFavicon(image); } }); } updateTaskDescription(); } /** * Destroys all dependent components of the task description helper. */ public void destroy() { mFaviconHelper.destroy(); if (mCurrentTab != null) { mCurrentTab.removeObserver(mTabObserver); } mTabModelSelector.removeObserver(mTabModelSelectorObserver); for (TabModel model : mTabModelSelector.getModels()) { model.removeObserver(mTabModelObserver); } } }