// 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);
}
}
}