OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.ntp; |
| 6 |
| 7 import android.app.Activity; |
| 8 import android.content.res.Resources; |
| 9 import android.graphics.Bitmap; |
| 10 import android.graphics.drawable.BitmapDrawable; |
| 11 import android.graphics.drawable.Drawable; |
| 12 import android.text.TextUtils; |
| 13 import android.util.LruCache; |
| 14 import android.view.ContextMenu; |
| 15 import android.view.LayoutInflater; |
| 16 import android.view.MenuItem; |
| 17 import android.view.MenuItem.OnMenuItemClickListener; |
| 18 import android.view.View; |
| 19 import android.view.ViewGroup; |
| 20 import android.widget.BaseExpandableListAdapter; |
| 21 import android.widget.TextView; |
| 22 |
| 23 import com.google.android.apps.chrome.R; |
| 24 |
| 25 import org.chromium.base.ApiCompatibilityUtils; |
| 26 import org.chromium.chrome.browser.ForeignSessionHelper.ForeignSession; |
| 27 import org.chromium.chrome.browser.ForeignSessionHelper.ForeignSessionTab; |
| 28 import org.chromium.chrome.browser.ForeignSessionHelper.ForeignSessionWindow; |
| 29 import org.chromium.chrome.browser.RecentlyClosedBridge.RecentlyClosedTab; |
| 30 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback; |
| 31 import org.chromium.ui.WindowOpenDisposition; |
| 32 import org.chromium.ui.base.DeviceFormFactor; |
| 33 |
| 34 import java.util.ArrayList; |
| 35 import java.util.List; |
| 36 import java.util.concurrent.TimeUnit; |
| 37 |
| 38 /** |
| 39 * Row adapter for presenting recently closed tabs, synced tabs from other devic
es, the sync or |
| 40 * sign in promo, and currently open tabs (only in document mode) in a grouped l
ist view. |
| 41 */ |
| 42 public class RecentTabsRowAdapter extends BaseExpandableListAdapter { |
| 43 private static final int MAX_NUM_FAVICONS_TO_CACHE = 256; |
| 44 |
| 45 private enum ChildType { |
| 46 NONE, DEFAULT_CONTENT, SYNC_PROMO |
| 47 } |
| 48 |
| 49 private enum GroupType { |
| 50 CONTENT, VISIBLE_SEPARATOR, INVISIBLE_SEPARATOR |
| 51 } |
| 52 |
| 53 private final Activity mActivity; |
| 54 private final ArrayList<Group> mGroups; |
| 55 private final Drawable mDefaultFavicon; |
| 56 private final RecentTabsManager mRecentTabsManager; |
| 57 private final RecentlyClosedTabsGroup mRecentlyClosedTabsGroup = new Recentl
yClosedTabsGroup(); |
| 58 private final SeparatorGroup mVisibleSeparatorGroup = new SeparatorGroup(tru
e); |
| 59 private final SeparatorGroup mInvisibleSeparatorGroup = new SeparatorGroup(f
alse); |
| 60 private final long mInitializationTimestamp; // Seconds since Unix epoch. |
| 61 private final FaviconCache mFaviconCache; |
| 62 private final int mFaviconSize; |
| 63 |
| 64 /** |
| 65 * A generic group of objects to be shown in the RecentTabsRowAdapter, such
as the list of |
| 66 * recently closed tabs. |
| 67 */ |
| 68 abstract class Group { |
| 69 /** |
| 70 * @return The type of group: GroupType.CONTENT or GroupType.SEPARATOR. |
| 71 */ |
| 72 abstract GroupType getGroupType(); |
| 73 |
| 74 /** |
| 75 * @return The number of children in this group. |
| 76 */ |
| 77 abstract int getChildrenCount(); |
| 78 |
| 79 /** |
| 80 * @return The child type. |
| 81 */ |
| 82 abstract ChildType getChildType(); |
| 83 |
| 84 |
| 85 /** |
| 86 * @return The child at the position childPosition. |
| 87 */ |
| 88 abstract Object getChild(int childPosition); |
| 89 |
| 90 /** |
| 91 * Returns the view corresponding to the child view at a given position. |
| 92 * |
| 93 * @param childPosition The position of the child. |
| 94 * @param isLastChild Whether this child is the last one. |
| 95 * @param convertView The re-usable child view (may be null). |
| 96 * @param parent The parent view group. |
| 97 * |
| 98 * @return The view corresponding to the child. |
| 99 */ |
| 100 View getChildView(int childPosition, boolean isLastChild, |
| 101 View convertView, ViewGroup parent) { |
| 102 View childView = convertView; |
| 103 if (childView == null) { |
| 104 LayoutInflater inflater = LayoutInflater.from(mActivity); |
| 105 childView = inflater.inflate(R.layout.recent_tabs_list_item, par
ent, false); |
| 106 |
| 107 ViewHolder viewHolder = new ViewHolder(); |
| 108 viewHolder.textView = (TextView) childView; |
| 109 childView.setTag(viewHolder); |
| 110 } |
| 111 |
| 112 ViewHolder viewHolder = (ViewHolder) childView.getTag(); |
| 113 configureChildView(childPosition, viewHolder); |
| 114 |
| 115 return childView; |
| 116 } |
| 117 |
| 118 /** |
| 119 * Configures a view inflated from recent_tabs_list_item.xml to display
information about |
| 120 * a child in this group. |
| 121 * |
| 122 * @param childPosition The position of the child within this group. |
| 123 * @param viewHolder The ViewHolder with references to pieces of the vie
w. |
| 124 */ |
| 125 abstract void configureChildView(int childPosition, ViewHolder viewHolde
r); |
| 126 |
| 127 /** |
| 128 * Returns the view corresponding to this group. |
| 129 * |
| 130 * @param isExpanded Whether the group is expanded. |
| 131 * @param convertView The re-usable group view (may be null). |
| 132 * @param parent The parent view group. |
| 133 * |
| 134 * @return The view corresponding to the group. |
| 135 */ |
| 136 public View getGroupView(boolean isExpanded, View convertView, ViewGroup
parent) { |
| 137 assert convertView == null || convertView instanceof RecentTabsGroup
View; |
| 138 RecentTabsGroupView groupView = (RecentTabsGroupView) convertView; |
| 139 if (groupView == null) { |
| 140 groupView = (RecentTabsGroupView) LayoutInflater.from(mActivity)
.inflate( |
| 141 R.layout.recent_tabs_group_item, parent, false); |
| 142 groupView.initialize(mInitializationTimestamp); |
| 143 } |
| 144 configureGroupView(groupView, isExpanded); |
| 145 return groupView; |
| 146 } |
| 147 |
| 148 /** |
| 149 * Configures an RecentTabsGroupView to display the header of this group
. |
| 150 * @param groupView The RecentTabsGroupView to configure. |
| 151 * @param isExpanded Whether the view is currently expanded. |
| 152 */ |
| 153 abstract void configureGroupView(RecentTabsGroupView groupView, boolean
isExpanded); |
| 154 |
| 155 /** |
| 156 * Sets whether this group is collapsed (i.e. whether only the header is
visible). |
| 157 */ |
| 158 abstract void setCollapsed(boolean isCollapsed); |
| 159 |
| 160 /** |
| 161 * @return Whether this group is collapsed. |
| 162 */ |
| 163 abstract boolean isCollapsed(); |
| 164 |
| 165 /** |
| 166 * Called when a child item is clicked. |
| 167 * @param childPosition The position of the child in the group. |
| 168 * @return Whether the click was handled. |
| 169 */ |
| 170 abstract boolean onChildClick(int childPosition); |
| 171 |
| 172 /** |
| 173 * Called when the context menu for the group view is being built. |
| 174 * @param menu The context menu being built. |
| 175 * @param activity The current activity. |
| 176 */ |
| 177 void onCreateContextMenuForGroup(ContextMenu menu, Activity activity) { |
| 178 } |
| 179 |
| 180 /** |
| 181 * Called when a context menu for one of the child views is being built. |
| 182 * @param childPosition The position of the child in the group. |
| 183 * @param menu The context menu being built. |
| 184 * @param activity The current activity. |
| 185 */ |
| 186 void onCreateContextMenuForChild(int childPosition, ContextMenu menu, |
| 187 Activity activity) { |
| 188 } |
| 189 } |
| 190 |
| 191 /** |
| 192 * A group containing all the tabs currently open on this device. |
| 193 */ |
| 194 class CurrentlyOpenTabsGroup extends Group { |
| 195 private static final int NUM_DEFAULT_VISIBLE_TABS = 6; |
| 196 |
| 197 private final List<CurrentlyOpenTab> mCurrentlyOpenTabs; |
| 198 private final boolean mShowingAll; |
| 199 |
| 200 public CurrentlyOpenTabsGroup(List<CurrentlyOpenTab> tabsList) { |
| 201 mCurrentlyOpenTabs = tabsList; |
| 202 mShowingAll = mRecentTabsManager.isCurrentlyOpenTabsShowingAll(); |
| 203 } |
| 204 |
| 205 private boolean isMoreButton(int childPosition) { |
| 206 return !mShowingAll && childPosition |
| 207 == Math.min(NUM_DEFAULT_VISIBLE_TABS, mCurrentlyOpenTabs.siz
e()); |
| 208 } |
| 209 |
| 210 @Override |
| 211 GroupType getGroupType() { |
| 212 return GroupType.CONTENT; |
| 213 } |
| 214 |
| 215 @Override |
| 216 int getChildrenCount() { |
| 217 if (mShowingAll) return mCurrentlyOpenTabs.size(); |
| 218 return Math.min(NUM_DEFAULT_VISIBLE_TABS, mCurrentlyOpenTabs.size()
- 1) + 1; |
| 219 } |
| 220 |
| 221 @Override |
| 222 ChildType getChildType() { |
| 223 return ChildType.DEFAULT_CONTENT; |
| 224 } |
| 225 |
| 226 @Override |
| 227 CurrentlyOpenTab getChild(int childPosition) { |
| 228 if (isMoreButton(childPosition)) return null; |
| 229 |
| 230 return mCurrentlyOpenTabs.get(childPosition); |
| 231 } |
| 232 |
| 233 @Override |
| 234 void configureChildView(int childPosition, ViewHolder viewHolder) { |
| 235 if (isMoreButton(childPosition)) { |
| 236 Resources resources = mActivity.getResources(); |
| 237 String text = resources.getString(R.string.recent_tabs_show_more
); |
| 238 viewHolder.textView.setText(text); |
| 239 Drawable drawable = ApiCompatibilityUtils.getDrawable( |
| 240 resources, R.drawable.more_horiz); |
| 241 ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicB
ounds( |
| 242 viewHolder.textView, drawable, null, null, null); |
| 243 } else { |
| 244 CurrentlyOpenTab openTab = getChild(childPosition); |
| 245 viewHolder.textView.setText(TextUtils.isEmpty(openTab.getTitle()
) ? openTab.getUrl() |
| 246 : openTab.getTitle()); |
| 247 loadLocalFavicon(viewHolder, openTab.getUrl()); |
| 248 } |
| 249 } |
| 250 |
| 251 @Override |
| 252 void configureGroupView(RecentTabsGroupView groupView, boolean isExpande
d) { |
| 253 groupView.configureForCurrentlyOpenTabs(isExpanded); |
| 254 } |
| 255 |
| 256 @Override |
| 257 void setCollapsed(boolean isCollapsed) { |
| 258 mRecentTabsManager.setCurrentlyOpenTabsCollapsed(isCollapsed); |
| 259 } |
| 260 |
| 261 @Override |
| 262 boolean isCollapsed() { |
| 263 return mRecentTabsManager.isCurrentlyOpenTabsCollapsed(); |
| 264 } |
| 265 |
| 266 @Override |
| 267 boolean onChildClick(int childPosition) { |
| 268 if (isMoreButton(childPosition)) { |
| 269 mRecentTabsManager.setCurrentlyOpenTabsShowAll(true); |
| 270 } else { |
| 271 getChild(childPosition).getRunnable().run(); |
| 272 } |
| 273 return true; |
| 274 } |
| 275 } |
| 276 |
| 277 /** |
| 278 * A group containing all the tabs associated with a foreign session from a
synced device. |
| 279 */ |
| 280 class ForeignSessionGroup extends Group { |
| 281 private final ForeignSession mForeignSession; |
| 282 |
| 283 ForeignSessionGroup(ForeignSession foreignSession) { |
| 284 mForeignSession = foreignSession; |
| 285 } |
| 286 |
| 287 @Override |
| 288 public GroupType getGroupType() { |
| 289 return GroupType.CONTENT; |
| 290 } |
| 291 |
| 292 @Override |
| 293 public int getChildrenCount() { |
| 294 int count = 0; |
| 295 for (ForeignSessionWindow window : mForeignSession.windows) { |
| 296 count += window.tabs.size(); |
| 297 } |
| 298 return count; |
| 299 } |
| 300 |
| 301 @Override |
| 302 public ChildType getChildType() { |
| 303 return ChildType.DEFAULT_CONTENT; |
| 304 } |
| 305 |
| 306 @Override |
| 307 public ForeignSessionTab getChild(int childPosition) { |
| 308 for (ForeignSessionWindow window : mForeignSession.windows) { |
| 309 if (childPosition < window.tabs.size()) { |
| 310 return window.tabs.get(childPosition); |
| 311 } |
| 312 childPosition -= window.tabs.size(); |
| 313 } |
| 314 assert false; |
| 315 return null; |
| 316 } |
| 317 |
| 318 @Override |
| 319 public void configureChildView(int childPosition, ViewHolder viewHolder)
{ |
| 320 ForeignSessionTab sessionTab = getChild(childPosition); |
| 321 viewHolder.textView.setText(TextUtils.isEmpty(sessionTab.title) ? se
ssionTab.url |
| 322 : sessionTab.title); |
| 323 loadSyncedFavicon(viewHolder, sessionTab.url); |
| 324 } |
| 325 |
| 326 @Override |
| 327 public void configureGroupView(RecentTabsGroupView groupView, boolean is
Expanded) { |
| 328 groupView.configureForForeignSession(mForeignSession, isExpanded); |
| 329 } |
| 330 |
| 331 @Override |
| 332 public void setCollapsed(boolean isCollapsed) { |
| 333 mRecentTabsManager.setForeignSessionCollapsed(mForeignSession, isCol
lapsed); |
| 334 } |
| 335 |
| 336 @Override |
| 337 public boolean isCollapsed() { |
| 338 return mRecentTabsManager.getForeignSessionCollapsed(mForeignSession
); |
| 339 } |
| 340 |
| 341 @Override |
| 342 public boolean onChildClick(int childPosition) { |
| 343 ForeignSessionTab foreignSessionTab = getChild(childPosition); |
| 344 mRecentTabsManager.openForeignSessionTab(mForeignSession, foreignSes
sionTab, |
| 345 WindowOpenDisposition.CURRENT_TAB); |
| 346 return true; |
| 347 } |
| 348 |
| 349 @Override |
| 350 public void onCreateContextMenuForGroup(ContextMenu menu, Activity activ
ity) { |
| 351 OnMenuItemClickListener listener = new OnMenuItemClickListener() { |
| 352 @Override |
| 353 public boolean onMenuItemClick(MenuItem item) { |
| 354 mRecentTabsManager.deleteForeignSession(mForeignSession); |
| 355 return true; |
| 356 } |
| 357 }; |
| 358 menu.add(R.string.ntp_recent_tabs_remove_menu_option) |
| 359 .setOnMenuItemClickListener(listener); |
| 360 } |
| 361 |
| 362 @Override |
| 363 public void onCreateContextMenuForChild(int childPosition, ContextMenu m
enu, |
| 364 Activity activity) { |
| 365 final ForeignSessionTab foreignSessionTab = getChild(childPosition); |
| 366 OnMenuItemClickListener listener = new OnMenuItemClickListener() { |
| 367 @Override |
| 368 public boolean onMenuItemClick(MenuItem item) { |
| 369 mRecentTabsManager.openForeignSessionTab(mForeignSession, fo
reignSessionTab, |
| 370 WindowOpenDisposition.NEW_BACKGROUND_TAB); |
| 371 return true; |
| 372 } |
| 373 }; |
| 374 menu.add(R.string.contextmenu_open_in_new_tab).setOnMenuItemClickLis
tener(listener); |
| 375 } |
| 376 } |
| 377 |
| 378 /** |
| 379 * A group containing tabs that were recently closed on this device and a li
nk to the history |
| 380 * page. |
| 381 */ |
| 382 class RecentlyClosedTabsGroup extends Group { |
| 383 private static final int ID_OPEN_IN_NEW_TAB = 1; |
| 384 private static final int ID_REMOVE_ALL = 2; |
| 385 |
| 386 @Override |
| 387 public GroupType getGroupType() { |
| 388 return GroupType.CONTENT; |
| 389 } |
| 390 |
| 391 @Override |
| 392 public int getChildrenCount() { |
| 393 // The number of children is the number of recently closed tabs, plu
s one for the "Show |
| 394 // full history" item. |
| 395 return 1 + mRecentTabsManager.getRecentlyClosedTabs().size(); |
| 396 } |
| 397 |
| 398 @Override |
| 399 public ChildType getChildType() { |
| 400 return ChildType.DEFAULT_CONTENT; |
| 401 } |
| 402 |
| 403 /** |
| 404 * @param childPosition The index of an item in the recently closed list
. |
| 405 * @return Whether the item at childPosition is the link to the history
page. |
| 406 */ |
| 407 private boolean isHistoryLink(int childPosition) { |
| 408 return childPosition == mRecentTabsManager.getRecentlyClosedTabs().s
ize(); |
| 409 } |
| 410 |
| 411 @Override |
| 412 public RecentlyClosedTab getChild(int childPosition) { |
| 413 if (isHistoryLink(childPosition)) { |
| 414 return null; |
| 415 } |
| 416 return mRecentTabsManager.getRecentlyClosedTabs().get(childPosition)
; |
| 417 } |
| 418 |
| 419 @Override |
| 420 public void configureChildView(int childPosition, ViewHolder viewHolder)
{ |
| 421 if (isHistoryLink(childPosition)) { |
| 422 viewHolder.textView.setText(R.string.show_full_history); |
| 423 ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicB
ounds( |
| 424 viewHolder.textView, R.drawable.history_favicon, 0, 0, 0
); |
| 425 } else { |
| 426 RecentlyClosedTab tab = getChild(childPosition); |
| 427 String title = NewTabPageView.getTitleForDisplay(tab.title, tab.
url); |
| 428 viewHolder.textView.setText(title); |
| 429 loadLocalFavicon(viewHolder, tab.url); |
| 430 } |
| 431 } |
| 432 |
| 433 @Override |
| 434 public void configureGroupView(RecentTabsGroupView groupView, boolean is
Expanded) { |
| 435 groupView.configureForRecentlyClosedTabs(isExpanded); |
| 436 } |
| 437 |
| 438 @Override |
| 439 public void setCollapsed(boolean isCollapsed) { |
| 440 mRecentTabsManager.setRecentlyClosedTabsCollapsed(isCollapsed); |
| 441 } |
| 442 |
| 443 @Override |
| 444 public boolean isCollapsed() { |
| 445 return mRecentTabsManager.isRecentlyClosedTabsCollapsed(); |
| 446 } |
| 447 |
| 448 @Override |
| 449 public boolean onChildClick(int childPosition) { |
| 450 if (isHistoryLink(childPosition)) { |
| 451 mRecentTabsManager.openHistoryPage(); |
| 452 } else { |
| 453 mRecentTabsManager.openRecentlyClosedTab(getChild(childPosition)
, |
| 454 WindowOpenDisposition.CURRENT_TAB); |
| 455 } |
| 456 return true; |
| 457 } |
| 458 |
| 459 @Override |
| 460 public void onCreateContextMenuForGroup(ContextMenu menu, Activity activ
ity) { |
| 461 } |
| 462 |
| 463 @Override |
| 464 public void onCreateContextMenuForChild(final int childPosition, Context
Menu menu, |
| 465 Activity activity) { |
| 466 final RecentlyClosedTab recentlyClosedTab = getChild(childPosition); |
| 467 if (recentlyClosedTab == null) return; |
| 468 OnMenuItemClickListener listener = new OnMenuItemClickListener() { |
| 469 @Override |
| 470 public boolean onMenuItemClick(MenuItem item) { |
| 471 switch (item.getItemId()) { |
| 472 case ID_REMOVE_ALL: |
| 473 mRecentTabsManager.clearRecentlyClosedTabs(); |
| 474 break; |
| 475 case ID_OPEN_IN_NEW_TAB: |
| 476 mRecentTabsManager.openRecentlyClosedTab(recentlyClo
sedTab, |
| 477 WindowOpenDisposition.NEW_BACKGROUND_TAB); |
| 478 break; |
| 479 default: |
| 480 assert false; |
| 481 } |
| 482 return true; |
| 483 } |
| 484 }; |
| 485 menu.add(ContextMenu.NONE, ID_OPEN_IN_NEW_TAB, ContextMenu.NONE, |
| 486 R.string.contextmenu_open_in_new_tab).setOnMenuItemClickList
ener(listener); |
| 487 menu.add(ContextMenu.NONE, ID_REMOVE_ALL, ContextMenu.NONE, |
| 488 R.string.remove_all).setOnMenuItemClickListener(listener); |
| 489 } |
| 490 } |
| 491 |
| 492 /** |
| 493 * A group containing a blank separator. |
| 494 */ |
| 495 class SeparatorGroup extends Group { |
| 496 private final boolean mIsVisible; |
| 497 |
| 498 public SeparatorGroup(boolean isVisible) { |
| 499 mIsVisible = isVisible; |
| 500 } |
| 501 |
| 502 @Override |
| 503 public GroupType getGroupType() { |
| 504 return mIsVisible ? GroupType.VISIBLE_SEPARATOR : GroupType.INVISIBL
E_SEPARATOR; |
| 505 } |
| 506 |
| 507 @Override |
| 508 public ChildType getChildType() { |
| 509 return ChildType.NONE; |
| 510 } |
| 511 |
| 512 @Override |
| 513 public int getChildrenCount() { |
| 514 return 0; |
| 515 } |
| 516 |
| 517 @Override |
| 518 public View getGroupView(boolean isExpanded, View convertView, ViewGroup
parent) { |
| 519 if (convertView == null) { |
| 520 int layout = mIsVisible |
| 521 ? R.layout.recent_tabs_group_separator_visible |
| 522 : R.layout.recent_tabs_group_separator_invisible; |
| 523 convertView = LayoutInflater.from(mActivity).inflate(layout, par
ent, false); |
| 524 } |
| 525 return convertView; |
| 526 } |
| 527 |
| 528 @Override |
| 529 public void configureGroupView(RecentTabsGroupView groupView, boolean is
Expanded) { |
| 530 } |
| 531 |
| 532 @Override |
| 533 public void configureChildView(int childPosition, ViewHolder viewHolder)
{ |
| 534 } |
| 535 |
| 536 @Override |
| 537 public Object getChild(int childPosition) { |
| 538 return null; |
| 539 } |
| 540 |
| 541 @Override |
| 542 public void setCollapsed(boolean isCollapsed) { |
| 543 } |
| 544 |
| 545 @Override |
| 546 public boolean isCollapsed() { |
| 547 return false; |
| 548 } |
| 549 |
| 550 @Override |
| 551 public boolean onChildClick(int childPosition) { |
| 552 return false; |
| 553 } |
| 554 } |
| 555 |
| 556 /** |
| 557 * A group containing the Sync Promo. |
| 558 */ |
| 559 class SyncPromoGroup extends Group { |
| 560 @Override |
| 561 public GroupType getGroupType() { |
| 562 return GroupType.CONTENT; |
| 563 } |
| 564 |
| 565 @Override |
| 566 public ChildType getChildType() { |
| 567 return ChildType.SYNC_PROMO; |
| 568 } |
| 569 |
| 570 @Override |
| 571 public int getChildrenCount() { |
| 572 return 1; |
| 573 } |
| 574 |
| 575 @Override |
| 576 public Object getChild(int childPosition) { |
| 577 return null; |
| 578 } |
| 579 |
| 580 @Override |
| 581 View getChildView(int childPosition, boolean isLastChild, View convertVi
ew, |
| 582 ViewGroup parent) { |
| 583 if (convertView == null) { |
| 584 convertView = new RecentTabsPromoView(mActivity, mRecentTabsMana
ger, null); |
| 585 } |
| 586 return convertView; |
| 587 } |
| 588 |
| 589 @Override |
| 590 public void configureChildView(int childPosition, ViewHolder viewHolder)
{ |
| 591 } |
| 592 |
| 593 @Override |
| 594 public void configureGroupView(RecentTabsGroupView groupView, boolean is
Expanded) { |
| 595 groupView.configureForSyncPromo(isExpanded); |
| 596 } |
| 597 |
| 598 @Override |
| 599 public void setCollapsed(boolean isCollapsed) { |
| 600 mRecentTabsManager.setSyncPromoCollapsed(isCollapsed); |
| 601 } |
| 602 |
| 603 @Override |
| 604 public boolean isCollapsed() { |
| 605 return mRecentTabsManager.isSyncPromoCollapsed(); |
| 606 } |
| 607 |
| 608 @Override |
| 609 public boolean onChildClick(int childPosition) { |
| 610 return false; |
| 611 } |
| 612 } |
| 613 |
| 614 private static class FaviconCache { |
| 615 private static final String SYNCED_FAVICON_PREFIX = "Synced"; |
| 616 private static final String LOCAL_FAVICON_PREFIX = "Local"; |
| 617 |
| 618 private final LruCache<String, Drawable> mMemoryCache; |
| 619 |
| 620 public FaviconCache(int size) { |
| 621 mMemoryCache = new LruCache<String, Drawable>(size); |
| 622 } |
| 623 |
| 624 public Drawable getSyncedFaviconImage(String url) { |
| 625 return mMemoryCache.get(SYNCED_FAVICON_PREFIX + url); |
| 626 } |
| 627 |
| 628 public void putSycnedFaviconImage(String url, Drawable image) { |
| 629 mMemoryCache.put(SYNCED_FAVICON_PREFIX + url, image); |
| 630 } |
| 631 |
| 632 public Drawable getLocalFaviconImage(String url) { |
| 633 return mMemoryCache.get(LOCAL_FAVICON_PREFIX + url); |
| 634 } |
| 635 |
| 636 public void putLocalFaviconImage(String url, Drawable image) { |
| 637 mMemoryCache.put(LOCAL_FAVICON_PREFIX + url, image); |
| 638 } |
| 639 } |
| 640 |
| 641 /** |
| 642 * Creates an RecentTabsRowAdapter used to populate an ExpandableList with o
ther |
| 643 * devices and foreign tab cells. |
| 644 * |
| 645 * @param activity The Android activity this adapter will work in. |
| 646 * @param recentTabsManager The RecentTabsManager that will act as the data
source. |
| 647 */ |
| 648 public RecentTabsRowAdapter(Activity activity, RecentTabsManager recentTabsM
anager) { |
| 649 mActivity = activity; |
| 650 mRecentTabsManager = recentTabsManager; |
| 651 mGroups = new ArrayList<Group>(); |
| 652 mInitializationTimestamp = |
| 653 TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLIS
ECONDS); |
| 654 mFaviconCache = buildFaviconCache(MAX_NUM_FAVICONS_TO_CACHE); |
| 655 |
| 656 Resources resources = activity.getResources(); |
| 657 mDefaultFavicon = ApiCompatibilityUtils.getDrawable(resources, R.drawabl
e.default_favicon); |
| 658 mFaviconSize = mDefaultFavicon.getIntrinsicHeight(); |
| 659 } |
| 660 |
| 661 private static FaviconCache buildFaviconCache(int size) { |
| 662 return new FaviconCache(size); |
| 663 } |
| 664 |
| 665 /** |
| 666 * ViewHolder class optimizes looking up table row fields. findViewById is o
nly called once |
| 667 * per row view initialization, and the references are cached here. Also sto
res a reference to |
| 668 * the favicon image callback; so that we can make sure we load the correct
favicon. |
| 669 */ |
| 670 private static class ViewHolder { |
| 671 public TextView textView; |
| 672 public FaviconImageCallback imageCallback; |
| 673 } |
| 674 |
| 675 private Drawable faviconDrawable(Bitmap image) { |
| 676 if (image == null) return null; |
| 677 return new BitmapDrawable(mActivity.getResources(), Bitmap.createScaledB
itmap(image, |
| 678 mFaviconSize, mFaviconSize, true)); |
| 679 } |
| 680 |
| 681 private void loadSyncedFavicon(final ViewHolder viewHolder, final String url
) { |
| 682 Drawable image = mFaviconCache.getSyncedFaviconImage(url); |
| 683 if (image == null) { |
| 684 image = faviconDrawable(mRecentTabsManager.getSyncedFaviconImageForU
RL(url)); |
| 685 image = (image == null) ? mDefaultFavicon : image; |
| 686 mFaviconCache.putSycnedFaviconImage(url, image); |
| 687 } |
| 688 ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(vi
ewHolder.textView, |
| 689 image, null, null, null); |
| 690 } |
| 691 |
| 692 private void loadLocalFavicon(final ViewHolder viewHolder, final String url)
{ |
| 693 Drawable image; |
| 694 if (url == null) { |
| 695 // URL is null for print jobs, for example. |
| 696 image = mDefaultFavicon; |
| 697 } else { |
| 698 image = mFaviconCache.getLocalFaviconImage(url); |
| 699 if (image == null) { |
| 700 FaviconImageCallback imageCallback = new FaviconImageCallback()
{ |
| 701 @Override |
| 702 public void onFaviconAvailable(Bitmap bitmap, String iconUrl
) { |
| 703 if (this != viewHolder.imageCallback) return; |
| 704 Drawable image = faviconDrawable(bitmap); |
| 705 image = (image == null) ? mDefaultFavicon : image; |
| 706 mFaviconCache.putLocalFaviconImage(url, image); |
| 707 ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIn
trinsicBounds( |
| 708 viewHolder.textView, image, null, null, null); |
| 709 } |
| 710 }; |
| 711 viewHolder.imageCallback = imageCallback; |
| 712 mRecentTabsManager.getLocalFaviconForUrl(url, mFaviconSize, imag
eCallback); |
| 713 image = mDefaultFavicon; |
| 714 } |
| 715 } |
| 716 ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(vi
ewHolder.textView, |
| 717 image, null, null, null); |
| 718 } |
| 719 |
| 720 @Override |
| 721 public View getChildView(int groupPosition, int childPosition, boolean isLas
tChild, |
| 722 View convertView, ViewGroup parent) { |
| 723 return getGroup(groupPosition).getChildView(childPosition, isLastChild,
convertView, |
| 724 parent); |
| 725 } |
| 726 |
| 727 // BaseExpandableListAdapter group related implementations |
| 728 @Override |
| 729 public int getGroupCount() { |
| 730 return mGroups.size(); |
| 731 } |
| 732 |
| 733 @Override |
| 734 public long getGroupId(int groupPosition) { |
| 735 return groupPosition; |
| 736 } |
| 737 |
| 738 @Override |
| 739 public Group getGroup(int groupPosition) { |
| 740 return mGroups.get(groupPosition); |
| 741 } |
| 742 |
| 743 @Override |
| 744 public View getGroupView(int groupPosition, boolean isExpanded, View convert
View, |
| 745 ViewGroup parent) { |
| 746 return getGroup(groupPosition).getGroupView(isExpanded, convertView, par
ent); |
| 747 } |
| 748 |
| 749 // BaseExpandableListAdapter child related implementations |
| 750 @Override |
| 751 public int getChildrenCount(int groupPosition) { |
| 752 return getGroup(groupPosition).getChildrenCount(); |
| 753 } |
| 754 |
| 755 @Override |
| 756 public long getChildId(int groupPosition, int childPosition) { |
| 757 return childPosition; |
| 758 } |
| 759 |
| 760 @Override |
| 761 public Object getChild(int groupPosition, int childPosition) { |
| 762 return getGroup(groupPosition).getChild(childPosition); |
| 763 } |
| 764 |
| 765 @Override |
| 766 public boolean isChildSelectable(int groupPosition, int childPosition) { |
| 767 return true; |
| 768 } |
| 769 |
| 770 // BaseExpandableListAdapter misc. implementation |
| 771 @Override |
| 772 public boolean hasStableIds() { |
| 773 return false; |
| 774 } |
| 775 |
| 776 @Override |
| 777 public int getGroupType(int groupPosition) { |
| 778 return getGroup(groupPosition).getGroupType().ordinal(); |
| 779 } |
| 780 |
| 781 @Override |
| 782 public int getGroupTypeCount() { |
| 783 return GroupType.values().length; |
| 784 } |
| 785 |
| 786 private void addGroup(Group group) { |
| 787 if (!DeviceFormFactor.isTablet(mActivity)) { |
| 788 mGroups.add(group); |
| 789 } else { |
| 790 if (mGroups.size() == 0) { |
| 791 mGroups.add(mInvisibleSeparatorGroup); |
| 792 } |
| 793 mGroups.add(group); |
| 794 mGroups.add(mInvisibleSeparatorGroup); |
| 795 } |
| 796 } |
| 797 |
| 798 @Override |
| 799 public void notifyDataSetChanged() { |
| 800 mGroups.clear(); |
| 801 List<CurrentlyOpenTab> tabList = mRecentTabsManager.getCurrentlyOpenTabs
(); |
| 802 if (tabList != null && !tabList.isEmpty()) { |
| 803 addGroup(new CurrentlyOpenTabsGroup(tabList)); |
| 804 } |
| 805 addGroup(mRecentlyClosedTabsGroup); |
| 806 for (ForeignSession session : mRecentTabsManager.getForeignSessions()) { |
| 807 addGroup(new ForeignSessionGroup(session)); |
| 808 } |
| 809 if (mRecentTabsManager.shouldDisplaySyncPromo()) { |
| 810 addGroup(new SyncPromoGroup()); |
| 811 } |
| 812 |
| 813 // Add separator line after the recently closed tabs group. |
| 814 int recentlyClosedIndex = mGroups.indexOf(mRecentlyClosedTabsGroup); |
| 815 if (DeviceFormFactor.isTablet(mActivity)) { |
| 816 if (recentlyClosedIndex != mGroups.size() - 2) { |
| 817 mGroups.set(recentlyClosedIndex + 1, mVisibleSeparatorGroup); |
| 818 } |
| 819 } else if (recentlyClosedIndex != mGroups.size() - 1) { |
| 820 mGroups.add(recentlyClosedIndex + 1, mVisibleSeparatorGroup); |
| 821 } |
| 822 |
| 823 super.notifyDataSetChanged(); |
| 824 } |
| 825 |
| 826 @Override |
| 827 public int getChildType(int groupPosition, int childPosition) { |
| 828 return mGroups.get(groupPosition).getChildType().ordinal(); |
| 829 } |
| 830 |
| 831 @Override |
| 832 public int getChildTypeCount() { |
| 833 return ChildType.values().length; |
| 834 } |
| 835 } |
OLD | NEW |