| // 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.customtabs; |
| |
| import android.graphics.Bitmap; |
| import android.text.TextUtils; |
| |
| import org.chromium.base.ApiCompatibilityUtils; |
| import org.chromium.chrome.browser.ChromeActivity; |
| 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.tab.TabThemeColorHelper; |
| import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; |
| import org.chromium.chrome.browser.tabmodel.TabSelectionType; |
| import org.chromium.chrome.browser.util.UrlUtilities; |
| import org.chromium.components.security_state.ConnectionSecurityLevel; |
| import org.chromium.content_public.browser.NavigationHandle; |
| |
| 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 CustomTabTaskDescriptionHelper { |
| private final int mDefaultThemeColor; |
| private final ChromeActivity mActivity; |
| private final TabModelSelector mTabModelSelector; |
| |
| private final CustomTabTaskDescriptionIconGenerator mIconGenerator; |
| private final FaviconHelper mFaviconHelper; |
| |
| private final TabModelSelectorObserver mTabModelSelectorObserver; |
| private final TabModelSelectorTabModelObserver 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 CustomTabTaskDescriptionHelper(ChromeActivity activity, int defaultThemeColor) { |
| mActivity = activity; |
| mDefaultThemeColor = defaultThemeColor; |
| |
| mTabModelSelector = mActivity.getTabModelSelector(); |
| |
| mIconGenerator = new CustomTabTaskDescriptionIconGenerator(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 onDidFinishNavigation(Tab tab, NavigationHandle navigation) { |
| if (navigation.hasCommitted() && navigation.isInMainFrame() |
| && !navigation.isSameDocument()) { |
| 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; |
| } |
| }; |
| |
| mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() { |
| @Override |
| public void onTabModelSelected(TabModel newModel, TabModel oldModel) { |
| refreshSelectedTab(); |
| } |
| }; |
| |
| mTabModelObserver = new TabModelSelectorTabModelObserver(mTabModelSelector) { |
| @Override |
| public void didSelectTab(Tab tab, @TabSelectionType int 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 multipleTabsPendingClosure(List<Tab> tabs, boolean isAllTabs) { |
| refreshSelectedTab(); |
| } |
| }; |
| |
| mTabModelSelector.addObserver(mTabModelSelectorObserver); |
| 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) { |
| if (!TabThemeColorHelper.isDefaultColorUsed(mCurrentTab)) { |
| color = TabThemeColorHelper.getColor(mCurrentTab); |
| } |
| } |
| 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, (image, 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); |
| mTabModelObserver.destroy(); |
| } |
| } |