// 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.ntp;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.LruCache;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
import org.chromium.chrome.browser.ntp.ForeignSessionHelper.ForeignSession;
import org.chromium.chrome.browser.ntp.ForeignSessionHelper.ForeignSessionTab;
import org.chromium.chrome.browser.ntp.ForeignSessionHelper.ForeignSessionWindow;
import org.chromium.chrome.browser.ntp.RecentlyClosedBridge.RecentlyClosedTab;
import org.chromium.chrome.browser.signin.SigninAccessPoint;
import org.chromium.chrome.browser.signin.SigninAndSyncView;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.mojom.WindowOpenDisposition;
import java.util.ArrayList;
import java.util.List;
/**
* Row adapter for presenting recently closed tabs, synced tabs from other devices, the sync or
* sign in promo, and currently open tabs (only in document mode) in a grouped list view.
*/
public class RecentTabsRowAdapter extends BaseExpandableListAdapter {
private static final int MAX_NUM_FAVICONS_TO_CACHE = 256;
private enum ChildType {
NONE, DEFAULT_CONTENT, SYNC_PROMO
}
private enum GroupType {
CONTENT, VISIBLE_SEPARATOR, INVISIBLE_SEPARATOR
}
// Values from the OtherSessionsActions enum in histograms.xml; do not change these values or
// histograms will be broken.
private static class OtherSessionsActions {
static final int MENU_INITIALIZED = 0;
static final int LINK_CLICKED = 2;
static final int COLLAPSE_SESSION = 6;
static final int EXPAND_SESSION = 7;
static final int OPEN_ALL = 8;
static final int HAS_FOREIGN_DATA = 9;
static final int HIDE_FOR_NOW = 10;
static final int LIMIT = 11;
}
private final Activity mActivity;
private final ArrayList<Group> mGroups;
private final Drawable mDefaultFavicon;
private final RecentTabsManager mRecentTabsManager;
private final RecentlyClosedTabsGroup mRecentlyClosedTabsGroup = new RecentlyClosedTabsGroup();
private final SeparatorGroup mVisibleSeparatorGroup = new SeparatorGroup(true);
private final SeparatorGroup mInvisibleSeparatorGroup = new SeparatorGroup(false);
private final FaviconCache mFaviconCache;
private final int mFaviconSize;
private boolean mHasForeignDataRecorded;
/**
* A generic group of objects to be shown in the RecentTabsRowAdapter, such as the list of
* recently closed tabs.
*/
abstract class Group {
/**
* @return The type of group: GroupType.CONTENT or GroupType.SEPARATOR.
*/
abstract GroupType getGroupType();
/**
* @return The number of children in this group.
*/
abstract int getChildrenCount();
/**
* @return The child type.
*/
abstract ChildType getChildType();
/**
* @return The child at the position childPosition.
*/
abstract Object getChild(int childPosition);
/**
* Returns the view corresponding to the child view at a given position.
*
* @param childPosition The position of the child.
* @param isLastChild Whether this child is the last one.
* @param convertView The re-usable child view (may be null).
* @param parent The parent view group.
*
* @return The view corresponding to the child.
*/
View getChildView(int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
View childView = convertView;
if (childView == null) {
LayoutInflater inflater = LayoutInflater.from(mActivity);
childView = inflater.inflate(R.layout.recent_tabs_list_item, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.textView = (TextView) childView;
childView.setTag(viewHolder);
}
ViewHolder viewHolder = (ViewHolder) childView.getTag();
configureChildView(childPosition, viewHolder);
return childView;
}
/**
* Configures a view inflated from recent_tabs_list_item.xml to display information about
* a child in this group.
*
* @param childPosition The position of the child within this group.
* @param viewHolder The ViewHolder with references to pieces of the view.
*/
abstract void configureChildView(int childPosition, ViewHolder viewHolder);
/**
* Returns the view corresponding to this group.
*
* @param isExpanded Whether the group is expanded.
* @param convertView The re-usable group view (may be null).
* @param parent The parent view group.
*
* @return The view corresponding to the group.
*/
public View getGroupView(boolean isExpanded, View convertView, ViewGroup parent) {
assert convertView == null || convertView instanceof RecentTabsGroupView;
RecentTabsGroupView groupView = (RecentTabsGroupView) convertView;
if (groupView == null) {
groupView = (RecentTabsGroupView) LayoutInflater.from(mActivity).inflate(
R.layout.recent_tabs_group_item, parent, false);
}
configureGroupView(groupView, isExpanded);
return groupView;
}
/**
* Configures an RecentTabsGroupView to display the header of this group.
* @param groupView The RecentTabsGroupView to configure.
* @param isExpanded Whether the view is currently expanded.
*/
abstract void configureGroupView(RecentTabsGroupView groupView, boolean isExpanded);
/**
* Sets whether this group is collapsed (i.e. whether only the header is visible).
*/
abstract void setCollapsed(boolean isCollapsed);
/**
* @return Whether this group is collapsed.
*/
abstract boolean isCollapsed();
/**
* Called when a child item is clicked.
* @param childPosition The position of the child in the group.
* @return Whether the click was handled.
*/
abstract boolean onChildClick(int childPosition);
/**
* Called when the context menu for the group view is being built.
* @param menu The context menu being built.
* @param activity The current activity.
*/
void onCreateContextMenuForGroup(ContextMenu menu, Activity activity) {
}
/**
* Called when a context menu for one of the child views is being built.
* @param childPosition The position of the child in the group.
* @param menu The context menu being built.
* @param activity The current activity.
*/
void onCreateContextMenuForChild(int childPosition, ContextMenu menu,
Activity activity) {
}
}
/**
* A group containing all the tabs currently open on this device.
*/
class CurrentlyOpenTabsGroup extends Group {
private static final int NUM_DEFAULT_VISIBLE_TABS = 6;
private final List<CurrentlyOpenTab> mCurrentlyOpenTabs;
private final boolean mShowingAll;
public CurrentlyOpenTabsGroup(List<CurrentlyOpenTab> tabsList) {
mCurrentlyOpenTabs = tabsList;
mShowingAll = mRecentTabsManager.isCurrentlyOpenTabsShowingAll();
}
private boolean isMoreButton(int childPosition) {
return !mShowingAll && childPosition
== Math.min(NUM_DEFAULT_VISIBLE_TABS, mCurrentlyOpenTabs.size());
}
@Override
GroupType getGroupType() {
return GroupType.CONTENT;
}
@Override
int getChildrenCount() {
if (mShowingAll) return mCurrentlyOpenTabs.size();
return Math.min(NUM_DEFAULT_VISIBLE_TABS, mCurrentlyOpenTabs.size() - 1) + 1;
}
@Override
ChildType getChildType() {
return ChildType.DEFAULT_CONTENT;
}
@Override
CurrentlyOpenTab getChild(int childPosition) {
if (isMoreButton(childPosition)) return null;
return mCurrentlyOpenTabs.get(childPosition);
}
@Override
void configureChildView(int childPosition, ViewHolder viewHolder) {
if (isMoreButton(childPosition)) {
Resources resources = mActivity.getResources();
String text = resources.getString(R.string.recent_tabs_show_more);
viewHolder.textView.setText(text);
Drawable drawable = ApiCompatibilityUtils.getDrawable(
resources, R.drawable.more_horiz);
ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
viewHolder.textView, drawable, null, null, null);
} else {
CurrentlyOpenTab openTab = getChild(childPosition);
viewHolder.textView.setText(TextUtils.isEmpty(openTab.getTitle()) ? openTab.getUrl()
: openTab.getTitle());
loadLocalFavicon(viewHolder, openTab.getUrl());
}
}
@Override
void configureGroupView(RecentTabsGroupView groupView, boolean isExpanded) {
groupView.configureForCurrentlyOpenTabs(isExpanded);
}
@Override
void setCollapsed(boolean isCollapsed) {
mRecentTabsManager.setCurrentlyOpenTabsCollapsed(isCollapsed);
}
@Override
boolean isCollapsed() {
return mRecentTabsManager.isCurrentlyOpenTabsCollapsed();
}
@Override
boolean onChildClick(int childPosition) {
if (isMoreButton(childPosition)) {
mRecentTabsManager.setCurrentlyOpenTabsShowAll(true);
} else {
getChild(childPosition).getRunnable().run();
}
return true;
}
@Override
void onCreateContextMenuForChild(final int childPosition, ContextMenu menu,
Activity activity) {
if (isMoreButton(childPosition)) return;
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
mRecentTabsManager.closeTab(getChild(childPosition));
return true;
}
};
menu.add(R.string.close_tab).setOnMenuItemClickListener(listener);
}
}
/**
* A group containing all the tabs associated with a foreign session from a synced device.
*/
class ForeignSessionGroup extends Group {
private final ForeignSession mForeignSession;
ForeignSessionGroup(ForeignSession foreignSession) {
mForeignSession = foreignSession;
}
@Override
public GroupType getGroupType() {
return GroupType.CONTENT;
}
@Override
public int getChildrenCount() {
int count = 0;
for (ForeignSessionWindow window : mForeignSession.windows) {
count += window.tabs.size();
}
return count;
}
@Override
public ChildType getChildType() {
return ChildType.DEFAULT_CONTENT;
}
@Override
public ForeignSessionTab getChild(int childPosition) {
for (ForeignSessionWindow window : mForeignSession.windows) {
if (childPosition < window.tabs.size()) {
return window.tabs.get(childPosition);
}
childPosition -= window.tabs.size();
}
assert false;
return null;
}
@Override
public void configureChildView(int childPosition, ViewHolder viewHolder) {
ForeignSessionTab sessionTab = getChild(childPosition);
viewHolder.textView.setText(TextUtils.isEmpty(sessionTab.title) ? sessionTab.url
: sessionTab.title);
loadSyncedFavicon(viewHolder, sessionTab.url);
}
@Override
public void configureGroupView(RecentTabsGroupView groupView, boolean isExpanded) {
groupView.configureForForeignSession(mForeignSession, isExpanded);
}
@Override
public void setCollapsed(boolean isCollapsed) {
if (isCollapsed) {
RecordHistogram.recordEnumeratedHistogram("HistoryPage.OtherDevicesMenu",
OtherSessionsActions.COLLAPSE_SESSION, OtherSessionsActions.LIMIT);
} else {
RecordHistogram.recordEnumeratedHistogram("HistoryPage.OtherDevicesMenu",
OtherSessionsActions.EXPAND_SESSION, OtherSessionsActions.LIMIT);
}
mRecentTabsManager.setForeignSessionCollapsed(mForeignSession, isCollapsed);
}
@Override
public boolean isCollapsed() {
return mRecentTabsManager.getForeignSessionCollapsed(mForeignSession);
}
@Override
public boolean onChildClick(int childPosition) {
RecordHistogram.recordEnumeratedHistogram("HistoryPage.OtherDevicesMenu",
OtherSessionsActions.LINK_CLICKED, OtherSessionsActions.LIMIT);
ForeignSessionTab foreignSessionTab = getChild(childPosition);
mRecentTabsManager.openForeignSessionTab(mForeignSession, foreignSessionTab,
WindowOpenDisposition.CURRENT_TAB);
return true;
}
@Override
public void onCreateContextMenuForGroup(ContextMenu menu, Activity activity) {
menu.add(R.string.recent_tabs_open_all_menu_option)
.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
RecordHistogram.recordEnumeratedHistogram(
"HistoryPage.OtherDevicesMenu", OtherSessionsActions.OPEN_ALL,
OtherSessionsActions.LIMIT);
openAllTabs();
return true;
}
});
menu.add(R.string.recent_tabs_hide_menu_option)
.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
RecordHistogram.recordEnumeratedHistogram(
"HistoryPage.OtherDevicesMenu",
OtherSessionsActions.HIDE_FOR_NOW, OtherSessionsActions.LIMIT);
mRecentTabsManager.deleteForeignSession(mForeignSession);
return true;
}
});
}
@Override
public void onCreateContextMenuForChild(int childPosition, ContextMenu menu,
Activity activity) {
final ForeignSessionTab foreignSessionTab = getChild(childPosition);
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
mRecentTabsManager.openForeignSessionTab(mForeignSession, foreignSessionTab,
WindowOpenDisposition.NEW_BACKGROUND_TAB);
return true;
}
};
menu.add(R.string.contextmenu_open_in_new_tab).setOnMenuItemClickListener(listener);
}
private void openAllTabs() {
ForeignSessionTab firstTab = null;
for (ForeignSessionWindow window : mForeignSession.windows) {
for (ForeignSessionTab tab : window.tabs) {
if (firstTab == null) {
firstTab = tab;
} else {
mRecentTabsManager.openForeignSessionTab(
mForeignSession, tab, WindowOpenDisposition.NEW_BACKGROUND_TAB);
}
}
}
// Open the first tab last because calls to openForeignSessionTab after one for
// CURRENT_TAB are ignored.
if (firstTab != null) {
mRecentTabsManager.openForeignSessionTab(
mForeignSession, firstTab, WindowOpenDisposition.CURRENT_TAB);
}
}
}
/**
* A group containing tabs that were recently closed on this device and a link to the history
* page.
*/
class RecentlyClosedTabsGroup extends Group {
private static final int ID_OPEN_IN_NEW_TAB = 1;
private static final int ID_REMOVE_ALL = 2;
@Override
public GroupType getGroupType() {
return GroupType.CONTENT;
}
@Override
public int getChildrenCount() {
// The number of children is the number of recently closed tabs, plus one for the "Show
// full history" item.
return 1 + mRecentTabsManager.getRecentlyClosedTabs().size();
}
@Override
public ChildType getChildType() {
return ChildType.DEFAULT_CONTENT;
}
/**
* @param childPosition The index of an item in the recently closed list.
* @return Whether the item at childPosition is the link to the history page.
*/
private boolean isHistoryLink(int childPosition) {
return childPosition == mRecentTabsManager.getRecentlyClosedTabs().size();
}
@Override
public RecentlyClosedTab getChild(int childPosition) {
if (isHistoryLink(childPosition)) {
return null;
}
return mRecentTabsManager.getRecentlyClosedTabs().get(childPosition);
}
@Override
public void configureChildView(int childPosition, ViewHolder viewHolder) {
if (isHistoryLink(childPosition)) {
viewHolder.textView.setText(R.string.show_full_history);
ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
viewHolder.textView, R.drawable.history_favicon, 0, 0, 0);
} else {
RecentlyClosedTab tab = getChild(childPosition);
String title = TitleUtil.getTitleForDisplay(tab.title, tab.url);
viewHolder.textView.setText(title);
loadLocalFavicon(viewHolder, tab.url);
}
}
@Override
public void configureGroupView(RecentTabsGroupView groupView, boolean isExpanded) {
groupView.configureForRecentlyClosedTabs(isExpanded);
}
@Override
public void setCollapsed(boolean isCollapsed) {
mRecentTabsManager.setRecentlyClosedTabsCollapsed(isCollapsed);
}
@Override
public boolean isCollapsed() {
return mRecentTabsManager.isRecentlyClosedTabsCollapsed();
}
@Override
public boolean onChildClick(int childPosition) {
if (isHistoryLink(childPosition)) {
mRecentTabsManager.openHistoryPage();
} else {
mRecentTabsManager.openRecentlyClosedTab(getChild(childPosition),
WindowOpenDisposition.CURRENT_TAB);
}
return true;
}
@Override
public void onCreateContextMenuForGroup(ContextMenu menu, Activity activity) {
}
@Override
public void onCreateContextMenuForChild(final int childPosition, ContextMenu menu,
Activity activity) {
final RecentlyClosedTab recentlyClosedTab = getChild(childPosition);
if (recentlyClosedTab == null) return;
OnMenuItemClickListener listener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case ID_REMOVE_ALL:
mRecentTabsManager.clearRecentlyClosedTabs();
break;
case ID_OPEN_IN_NEW_TAB:
mRecentTabsManager.openRecentlyClosedTab(recentlyClosedTab,
WindowOpenDisposition.NEW_BACKGROUND_TAB);
break;
default:
assert false;
}
return true;
}
};
menu.add(ContextMenu.NONE, ID_OPEN_IN_NEW_TAB, ContextMenu.NONE,
R.string.contextmenu_open_in_new_tab).setOnMenuItemClickListener(listener);
menu.add(ContextMenu.NONE, ID_REMOVE_ALL, ContextMenu.NONE,
R.string.remove_all).setOnMenuItemClickListener(listener);
}
}
/**
* A group containing a blank separator.
*/
class SeparatorGroup extends Group {
private final boolean mIsVisible;
public SeparatorGroup(boolean isVisible) {
mIsVisible = isVisible;
}
@Override
public GroupType getGroupType() {
return mIsVisible ? GroupType.VISIBLE_SEPARATOR : GroupType.INVISIBLE_SEPARATOR;
}
@Override
public ChildType getChildType() {
return ChildType.NONE;
}
@Override
public int getChildrenCount() {
return 0;
}
@Override
public View getGroupView(boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
int layout = mIsVisible
? R.layout.recent_tabs_group_separator_visible
: R.layout.recent_tabs_group_separator_invisible;
convertView = LayoutInflater.from(mActivity).inflate(layout, parent, false);
}
return convertView;
}
@Override
public void configureGroupView(RecentTabsGroupView groupView, boolean isExpanded) {
}
@Override
public void configureChildView(int childPosition, ViewHolder viewHolder) {
}
@Override
public Object getChild(int childPosition) {
return null;
}
@Override
public void setCollapsed(boolean isCollapsed) {
}
@Override
public boolean isCollapsed() {
return false;
}
@Override
public boolean onChildClick(int childPosition) {
return false;
}
}
/**
* A group containing the Sync Promo.
*/
class SyncPromoGroup extends Group {
@Override
public GroupType getGroupType() {
return GroupType.CONTENT;
}
@Override
public ChildType getChildType() {
return ChildType.SYNC_PROMO;
}
@Override
public int getChildrenCount() {
return 1;
}
@Override
public Object getChild(int childPosition) {
return null;
}
@Override
View getChildView(int childPosition, boolean isLastChild, View convertView,
ViewGroup parent) {
if (convertView == null) {
SigninAndSyncView.Listener listener = new SigninAndSyncView.Listener() {
@Override
public void onViewDismissed() {
mRecentTabsManager.setSigninPromoDeclined();
notifyDataSetChanged();
}
};
convertView =
SigninAndSyncView.create(parent, listener, SigninAccessPoint.RECENT_TABS);
}
if (!mRecentTabsManager.isSignedIn()) {
RecordUserAction.record("Signin_Impression_FromRecentTabs");
}
return convertView;
}
@Override
public void configureChildView(int childPosition, ViewHolder viewHolder) {
}
@Override
public void configureGroupView(RecentTabsGroupView groupView, boolean isExpanded) {
groupView.configureForSyncPromo(isExpanded);
}
@Override
public void setCollapsed(boolean isCollapsed) {
mRecentTabsManager.setSyncPromoCollapsed(isCollapsed);
}
@Override
public boolean isCollapsed() {
return mRecentTabsManager.isSyncPromoCollapsed();
}
@Override
public boolean onChildClick(int childPosition) {
return false;
}
}
private static class FaviconCache {
private static final String SYNCED_FAVICON_PREFIX = "Synced";
private static final String LOCAL_FAVICON_PREFIX = "Local";
private final LruCache<String, Drawable> mMemoryCache;
public FaviconCache(int size) {
mMemoryCache = new LruCache<String, Drawable>(size);
}
public Drawable getSyncedFaviconImage(String url) {
return mMemoryCache.get(SYNCED_FAVICON_PREFIX + url);
}
public void putSycnedFaviconImage(String url, Drawable image) {
mMemoryCache.put(SYNCED_FAVICON_PREFIX + url, image);
}
public Drawable getLocalFaviconImage(String url) {
return mMemoryCache.get(LOCAL_FAVICON_PREFIX + url);
}
public void putLocalFaviconImage(String url, Drawable image) {
mMemoryCache.put(LOCAL_FAVICON_PREFIX + url, image);
}
}
/**
* Creates an RecentTabsRowAdapter used to populate an ExpandableList with other
* devices and foreign tab cells.
*
* @param activity The Android activity this adapter will work in.
* @param recentTabsManager The RecentTabsManager that will act as the data source.
*/
public RecentTabsRowAdapter(Activity activity, RecentTabsManager recentTabsManager) {
mActivity = activity;
mRecentTabsManager = recentTabsManager;
mGroups = new ArrayList<Group>();
mFaviconCache = buildFaviconCache(MAX_NUM_FAVICONS_TO_CACHE);
Resources resources = activity.getResources();
mDefaultFavicon = ApiCompatibilityUtils.getDrawable(resources, R.drawable.default_favicon);
mFaviconSize = resources.getDimensionPixelSize(R.dimen.default_favicon_size);
RecordHistogram.recordEnumeratedHistogram("HistoryPage.OtherDevicesMenu",
OtherSessionsActions.MENU_INITIALIZED, OtherSessionsActions.LIMIT);
}
private static FaviconCache buildFaviconCache(int size) {
return new FaviconCache(size);
}
/**
* ViewHolder class optimizes looking up table row fields. findViewById is only called once
* per row view initialization, and the references are cached here. Also stores a reference to
* the favicon image callback; so that we can make sure we load the correct favicon.
*/
private static class ViewHolder {
public TextView textView;
public FaviconImageCallback imageCallback;
}
private Drawable faviconDrawable(Bitmap image) {
if (image == null) return null;
return new BitmapDrawable(mActivity.getResources(), Bitmap.createScaledBitmap(image,
mFaviconSize, mFaviconSize, true));
}
private void loadSyncedFavicon(final ViewHolder viewHolder, final String url) {
Drawable image = mFaviconCache.getSyncedFaviconImage(url);
if (image == null) {
image = faviconDrawable(mRecentTabsManager.getSyncedFaviconImageForURL(url));
image = (image == null) ? mDefaultFavicon : image;
mFaviconCache.putSycnedFaviconImage(url, image);
}
ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(viewHolder.textView,
image, null, null, null);
}
private void loadLocalFavicon(final ViewHolder viewHolder, final String url) {
Drawable image;
if (url == null) {
// URL is null for print jobs, for example.
image = mDefaultFavicon;
} else {
image = mFaviconCache.getLocalFaviconImage(url);
if (image == null) {
FaviconImageCallback imageCallback = new FaviconImageCallback() {
@Override
public void onFaviconAvailable(Bitmap bitmap, String iconUrl) {
if (this != viewHolder.imageCallback) return;
Drawable image = faviconDrawable(bitmap);
image = (image == null) ? mDefaultFavicon : image;
mFaviconCache.putLocalFaviconImage(url, image);
ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
viewHolder.textView, image, null, null, null);
}
};
viewHolder.imageCallback = imageCallback;
mRecentTabsManager.getLocalFaviconForUrl(url, mFaviconSize, imageCallback);
image = mDefaultFavicon;
}
}
ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(viewHolder.textView,
image, null, null, null);
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
return getGroup(groupPosition).getChildView(childPosition, isLastChild, convertView,
parent);
}
// BaseExpandableListAdapter group related implementations
@Override
public int getGroupCount() {
return mGroups.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public Group getGroup(int groupPosition) {
return mGroups.get(groupPosition);
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
return getGroup(groupPosition).getGroupView(isExpanded, convertView, parent);
}
// BaseExpandableListAdapter child related implementations
@Override
public int getChildrenCount(int groupPosition) {
return getGroup(groupPosition).getChildrenCount();
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return getGroup(groupPosition).getChild(childPosition);
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
// BaseExpandableListAdapter misc. implementation
@Override
public boolean hasStableIds() {
return false;
}
@Override
public int getGroupType(int groupPosition) {
return getGroup(groupPosition).getGroupType().ordinal();
}
@Override
public int getGroupTypeCount() {
return GroupType.values().length;
}
private void addGroup(Group group) {
if (!DeviceFormFactor.isTablet(mActivity)) {
mGroups.add(group);
} else {
if (mGroups.size() == 0) {
mGroups.add(mInvisibleSeparatorGroup);
}
mGroups.add(group);
mGroups.add(mInvisibleSeparatorGroup);
}
}
@Override
public void notifyDataSetChanged() {
mGroups.clear();
List<CurrentlyOpenTab> tabList = mRecentTabsManager.getCurrentlyOpenTabs();
if (tabList != null && !tabList.isEmpty()) {
addGroup(new CurrentlyOpenTabsGroup(tabList));
}
addGroup(mRecentlyClosedTabsGroup);
for (ForeignSession session : mRecentTabsManager.getForeignSessions()) {
if (!mHasForeignDataRecorded) {
RecordHistogram.recordEnumeratedHistogram("HistoryPage.OtherDevicesMenu",
OtherSessionsActions.HAS_FOREIGN_DATA, OtherSessionsActions.LIMIT);
mHasForeignDataRecorded = true;
}
addGroup(new ForeignSessionGroup(session));
}
if (mRecentTabsManager.shouldDisplaySyncPromo()) {
addGroup(new SyncPromoGroup());
}
// Add separator line after the recently closed tabs group.
int recentlyClosedIndex = mGroups.indexOf(mRecentlyClosedTabsGroup);
if (DeviceFormFactor.isTablet(mActivity)) {
if (recentlyClosedIndex != mGroups.size() - 2) {
mGroups.set(recentlyClosedIndex + 1, mVisibleSeparatorGroup);
}
} else if (recentlyClosedIndex != mGroups.size() - 1) {
mGroups.add(recentlyClosedIndex + 1, mVisibleSeparatorGroup);
}
super.notifyDataSetChanged();
}
@Override
public int getChildType(int groupPosition, int childPosition) {
return mGroups.get(groupPosition).getChildType().ordinal();
}
@Override
public int getChildTypeCount() {
return ChildType.values().length;
}
}