Commit 073531e6 authored by David Trainor's avatar David Trainor Committed by Commit Bot

Add support for in-progress image downloads in DHv2

This includes adding AspectRatioFrameLayout instead of
SquareAsyncImageView (more properly handles the requirements of making
Views square).  This should be removed once we get proper support for
ConstraintLayout.

BUG=890530

Change-Id: I8a8dc7ae0772785a4cd0e11f555dfde53036a407
Reviewed-on: https://chromium-review.googlesource.com/c/1252332
Commit-Queue: David Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarShakti Sahu <shaktisahu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596031}
parent 7a01858a
...@@ -530,10 +530,8 @@ ...@@ -530,10 +530,8 @@
<dimen name="chip_list_padding">2.5dp</dimen> <dimen name="chip_list_padding">2.5dp</dimen>
<!-- Download manager dimensions --> <!-- Download manager dimensions -->
<dimen name="download_manager_image_width">150dp</dimen> <dimen name="download_manager_ideal_image_width">150dp</dimen>
<dimen name="download_manager_image_padding">2dp</dimen> <dimen name="download_manager_image_padding">2dp</dimen>
<dimen name="download_manager_generic_thumbnail_size">36dp</dimen>
<dimen name="download_manager_prefetch_thumbnail_size">114dp</dimen>
<dimen name="download_manager_prefetch_horizontal_margin">16dp</dimen> <dimen name="download_manager_prefetch_horizontal_margin">16dp</dimen>
<dimen name="download_manager_prefetch_vertical_margin">12dp</dimen> <dimen name="download_manager_prefetch_vertical_margin">12dp</dimen>
<dimen name="download_manager_section_title_padding_top">16dp</dimen> <dimen name="download_manager_section_title_padding_top">16dp</dimen>
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 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. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/ic_pause_white_24dp" />
<item>
<shape
android:innerRadius="14dp"
android:thickness="0dp"
android:shape="ring"
android:useLevel="false">
<stroke
android:width="2dp"
android:color="@android:color/white" />
</shape>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 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. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/ic_play_arrow_white_24dp" />
<item>
<shape
android:innerRadius="14dp"
android:thickness="0dp"
android:shape="ring"
android:useLevel="false">
<stroke
android:width="2dp"
android:color="@android:color/white" />
</shape>
</item>
</layer-list>
\ No newline at end of file
...@@ -16,23 +16,22 @@ ...@@ -16,23 +16,22 @@
app:columnCount="3" app:columnCount="3"
app:rowCount="2"> app:rowCount="2">
<android.support.v7.widget.AppCompatImageView <org.chromium.chrome.browser.download.home.list.view.AsyncImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="@dimen/download_manager_generic_thumbnail_size" android:layout_width="36dp"
android:layout_height="@dimen/download_manager_generic_thumbnail_size" android:layout_height="36dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:scaleType="center" android:scaleType="center"
app:layout_column="0" app:layout_column="0"
app:layout_row="0" app:layout_row="0"
app:layout_rowSpan="2" app:layout_rowSpan="2"
app:layout_gravity="center_vertical" app:layout_gravity="center_vertical" />
app:tint="@color/dark_mode_tint" />
<org.chromium.chrome.browser.download.home.view.SelectionView <org.chromium.chrome.browser.download.home.view.SelectionView
android:id="@+id/selection" android:id="@+id/selection"
android:layout_width="@dimen/download_manager_generic_thumbnail_size" android:layout_width="36dp"
android:layout_height="@dimen/download_manager_generic_thumbnail_size" android:layout_height="36dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
app:layout_column="0" app:layout_column="0"
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
found in the LICENSE file. found in the LICENSE file.
--> -->
<FrameLayout <org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
...@@ -13,19 +13,18 @@ ...@@ -13,19 +13,18 @@
android:clickable="true" android:clickable="true"
android:background="@color/modern_grey_100" > android:background="@color/modern_grey_100" >
<org.chromium.chrome.browser.download.home.list.view.SquareAsyncImageView <org.chromium.chrome.browser.download.home.list.view.AsyncImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/modern_grey_300" android:background="@color/modern_grey_300"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:layout_gravity="center" android:layout_gravity="center"
android:adjustViewBounds="true" android:adjustViewBounds="true"
style="@style/AsyncImageView" style="@style/AsyncImageView"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription"
app:layout_aspectRatio="100%" />
<org.chromium.chrome.browser.download.home.view.SelectionView <org.chromium.chrome.browser.download.home.view.SelectionView
android:id="@+id/selection" android:id="@+id/selection"
style="@style/DownloadItemSelectionView"/> style="@style/DownloadItemSelectionView"/>
</org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout>
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 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.
-->
<org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:background="@color/modern_grey_100" >
<!-- Set the src attribute in Java to wrap the drawable properly. -->
<ImageView
android:id="@+id/placeholder"
android:layout_width="match_parent"
app:layout_aspectRatio="100%"
android:scaleType="centerCrop"
android:layout_gravity="center"
android:adjustViewBounds="true"
tools:ignore="ContentDescription" />
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/cancel_button"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="end|top"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/download_notification_cancel_button"
android:src="@drawable/btn_close"
app:tint="@color/modern_blue_600" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal|bottom"
android:orientation="horizontal">
<TextView
android:id="@+id/caption"
style="@style/DownloadItemText"
android:textAppearance="@style/BlackHint2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:paddingStart="4dp" />
<org.chromium.chrome.browser.download.home.list.view.CircularProgressView
android:id="@+id/action_button"
android:layout_gravity="center_vertical"
style="@style/TinyCircularProgress" />
</LinearLayout>
</org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout>
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
app:columnCount="2" app:columnCount="2"
app:rowCount="3"> app:rowCount="3">
<!-- Set the src attribute in Java to wrap the drawable properly. -->
<org.chromium.chrome.browser.download.home.list.view.ForegroundRoundedCornerImageView <org.chromium.chrome.browser.download.home.list.view.ForegroundRoundedCornerImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="match_parent" android:layout_width="match_parent"
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
app:columnCount="4" app:columnCount="4"
app:rowCount="4"> app:rowCount="4">
<org.chromium.ui.widget.RoundedCornerImageView <org.chromium.chrome.browser.download.home.list.view.AsyncImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="113dp" android:layout_width="113dp"
android:layout_height="112dp" android:layout_height="112dp"
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
app:layout_rowSpan="4" app:layout_rowSpan="4"
app:cornerRadiusTopStart="@dimen/download_manager_prefetch_thumbnail_corner_radius" app:cornerRadiusTopStart="@dimen/download_manager_prefetch_thumbnail_corner_radius"
app:cornerRadiusBottomStart="@dimen/download_manager_prefetch_thumbnail_corner_radius" app:cornerRadiusBottomStart="@dimen/download_manager_prefetch_thumbnail_corner_radius"
style="@style/AsyncImageView"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<org.chromium.chrome.browser.download.home.view.SelectionView <org.chromium.chrome.browser.download.home.view.SelectionView
......
...@@ -18,4 +18,7 @@ ...@@ -18,4 +18,7 @@
<declare-styleable name="ForegroundRoundedCornerImageView"> <declare-styleable name="ForegroundRoundedCornerImageView">
<attr name="foregroundCompat" format="reference" /> <attr name="foregroundCompat" format="reference" />
</declare-styleable> </declare-styleable>
<declare-styleable name="AspectRatioFrameLayout_Layout">
<attr name="layout_aspectRatio" format="fraction" />
</declare-styleable>
</resources> </resources>
\ No newline at end of file
...@@ -74,6 +74,16 @@ ...@@ -74,6 +74,16 @@
</style> </style>
<!-- Download Home V2 Snowflake Contender Styles --> <!-- Download Home V2 Snowflake Contender Styles -->
<style name="TinyCircularProgress">
<item name="android:layout_width">36dp</item>
<item name="android:layout_height">36dp</item>
<item name="android:tint">@color/modern_blue_600</item>
<item name="android:background">?attr/selectableItemBackground</item>
<item name="resumeSrc">@drawable/circular_progress_bar_resume_tiny</item>
<!-- TODO(883387): Support retrying failed downloads. -->
<item name="retrySrc">@drawable/circular_progress_bar_resume_tiny</item>
<item name="pauseSrc">@drawable/circular_progress_bar_pause_tiny</item>
</style>
<style name="SmallCircularProgress"> <style name="SmallCircularProgress">
<item name="android:layout_width">36dp</item> <item name="android:layout_width">36dp</item>
<item name="android:layout_height">36dp</item> <item name="android:layout_height">36dp</item>
......
...@@ -71,7 +71,7 @@ public class ThumbnailRequestGlue implements ThumbnailRequest { ...@@ -71,7 +71,7 @@ public class ThumbnailRequestGlue implements ThumbnailRequest {
@Override @Override
public boolean getThumbnail(Callback<Bitmap> callback) { public boolean getThumbnail(Callback<Bitmap> callback) {
return mProvider.getVisualsForItem(mItem.id, (id, visuals) -> { return mProvider.getVisualsForItem(mItem.id, (id, visuals) -> {
if (visuals == null) { if (visuals == null || visuals.icon == null) {
callback.onResult(null); callback.onResult(null);
} else { } else {
callback.onResult(Bitmap.createScaledBitmap( callback.onResult(Bitmap.createScaledBitmap(
......
...@@ -288,7 +288,7 @@ class DateOrderedListMediator { ...@@ -288,7 +288,7 @@ class DateOrderedListMediator {
private Runnable getVisuals( private Runnable getVisuals(
OfflineItem item, int iconWidthPx, int iconHeightPx, VisualsCallback callback) { OfflineItem item, int iconWidthPx, int iconHeightPx, VisualsCallback callback) {
if (!UiUtils.canHaveThumbnails(item)) { if (!UiUtils.canHaveThumbnails(item) || iconWidthPx == 0 || iconHeightPx == 0) {
mHandler.post(() -> callback.onVisualsAvailable(item.id, null)); mHandler.post(() -> callback.onVisualsAvailable(item.id, null));
return () -> {}; return () -> {};
} }
......
...@@ -28,7 +28,7 @@ import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter; ...@@ -28,7 +28,7 @@ import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
class DateOrderedListView { class DateOrderedListView {
private final DecoratedListItemModel mModel; private final DecoratedListItemModel mModel;
private final int mImageWidthPx; private final int mIdealImageWidthPx;
private final int mImagePaddingPx; private final int mImagePaddingPx;
private final int mPrefetchVerticalPaddingPx; private final int mPrefetchVerticalPaddingPx;
private final int mPrefetchHorizontalPaddingPx; private final int mPrefetchHorizontalPaddingPx;
...@@ -66,8 +66,8 @@ class DateOrderedListView { ...@@ -66,8 +66,8 @@ class DateOrderedListView {
DateOrderedListObserver dateOrderedListObserver) { DateOrderedListObserver dateOrderedListObserver) {
mModel = model; mModel = model;
mImageWidthPx = mIdealImageWidthPx = context.getResources().getDimensionPixelSize(
context.getResources().getDimensionPixelSize(R.dimen.download_manager_image_width); R.dimen.download_manager_ideal_image_width);
mImagePaddingPx = context.getResources().getDimensionPixelOffset( mImagePaddingPx = context.getResources().getDimensionPixelOffset(
R.dimen.download_manager_image_padding); R.dimen.download_manager_image_padding);
mPrefetchHorizontalPaddingPx = context.getResources().getDimensionPixelSize( mPrefetchHorizontalPaddingPx = context.getResources().getDimensionPixelSize(
...@@ -116,7 +116,7 @@ class DateOrderedListView { ...@@ -116,7 +116,7 @@ class DateOrderedListView {
assert getOrientation() == VERTICAL; assert getOrientation() == VERTICAL;
int availableWidth = getWidth() - mImagePaddingPx; int availableWidth = getWidth() - mImagePaddingPx;
int columnWidth = mImageWidthPx - mImagePaddingPx; int columnWidth = mIdealImageWidthPx - mImagePaddingPx;
int easyFitSpan = availableWidth / columnWidth; int easyFitSpan = availableWidth / columnWidth;
double remaining = double remaining =
...@@ -145,6 +145,7 @@ class DateOrderedListView { ...@@ -145,6 +145,7 @@ class DateOrderedListView {
switch (ListUtils.getViewTypeForItem(mModel.get(position))) { switch (ListUtils.getViewTypeForItem(mModel.get(position))) {
case ListUtils.ViewType.IMAGE: case ListUtils.ViewType.IMAGE:
case ListUtils.ViewType.IN_PROGRESS_IMAGE:
outRect.left = mImagePaddingPx; outRect.left = mImagePaddingPx;
outRect.right = mImagePaddingPx; outRect.right = mImagePaddingPx;
outRect.top = mImagePaddingPx; outRect.top = mImagePaddingPx;
......
...@@ -26,7 +26,8 @@ public class ListUtils { ...@@ -26,7 +26,8 @@ public class ListUtils {
/** The potential types of list items that could be displayed. */ /** The potential types of list items that could be displayed. */
@IntDef({ViewType.DATE, ViewType.IN_PROGRESS, ViewType.GENERIC, ViewType.VIDEO, ViewType.IMAGE, @IntDef({ViewType.DATE, ViewType.IN_PROGRESS, ViewType.GENERIC, ViewType.VIDEO, ViewType.IMAGE,
ViewType.CUSTOM_VIEW, ViewType.PREFETCH, ViewType.SECTION_HEADER, ViewType.CUSTOM_VIEW, ViewType.PREFETCH, ViewType.SECTION_HEADER,
ViewType.SEPARATOR_DATE, ViewType.SEPARATOR_SECTION, ViewType.IN_PROGRESS_VIDEO}) ViewType.SEPARATOR_DATE, ViewType.SEPARATOR_SECTION, ViewType.IN_PROGRESS_VIDEO,
ViewType.IN_PROGRESS_IMAGE})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface ViewType { public @interface ViewType {
int DATE = 0; int DATE = 0;
...@@ -40,6 +41,7 @@ public class ListUtils { ...@@ -40,6 +41,7 @@ public class ListUtils {
int SEPARATOR_DATE = 8; int SEPARATOR_DATE = 8;
int SEPARATOR_SECTION = 9; int SEPARATOR_SECTION = 9;
int IN_PROGRESS_VIDEO = 10; int IN_PROGRESS_VIDEO = 10;
int IN_PROGRESS_IMAGE = 11;
} }
/** Converts a given list of {@link ListItem}s to a list of {@link OfflineItem}s. */ /** Converts a given list of {@link ListItem}s to a list of {@link OfflineItem}s. */
...@@ -84,7 +86,7 @@ public class ListUtils { ...@@ -84,7 +86,7 @@ public class ListUtils {
case OfflineItemFilter.FILTER_VIDEO: case OfflineItemFilter.FILTER_VIDEO:
return inProgress ? ViewType.IN_PROGRESS_VIDEO : ViewType.VIDEO; return inProgress ? ViewType.IN_PROGRESS_VIDEO : ViewType.VIDEO;
case OfflineItemFilter.FILTER_IMAGE: case OfflineItemFilter.FILTER_IMAGE:
return inProgress ? ViewType.IN_PROGRESS : ViewType.IMAGE; return inProgress ? ViewType.IN_PROGRESS_IMAGE : ViewType.IMAGE;
// case OfflineItemFilter.FILTER_PAGE: // case OfflineItemFilter.FILTER_PAGE:
// case OfflineItemFilter.FILTER_AUDIO: // case OfflineItemFilter.FILTER_AUDIO:
// case OfflineItemFilter.FILTER_OTHER: // case OfflineItemFilter.FILTER_OTHER:
...@@ -137,6 +139,12 @@ public class ListUtils { ...@@ -137,6 +139,12 @@ public class ListUtils {
return spanCount; return spanCount;
} }
return getViewTypeForItem(item) == ViewType.IMAGE ? 1 : spanCount; switch (getViewTypeForItem(item)) {
case ViewType.IMAGE: // Intentional fallthrough.
case ViewType.IN_PROGRESS_IMAGE:
return 1;
default:
return spanCount;
}
} }
} }
...@@ -5,58 +5,50 @@ ...@@ -5,58 +5,50 @@
package org.chromium.chrome.browser.download.home.list.holder; package org.chromium.chrome.browser.download.home.list.holder;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable; import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.support.v4.widget.ImageViewCompat;
import android.support.v7.content.res.AppCompatResources; import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.AppCompatImageView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.browser.download.home.list.ListItem; import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.UiUtils; import org.chromium.chrome.browser.download.home.list.UiUtils;
import org.chromium.chrome.browser.download.home.view.SelectionView;
import org.chromium.chrome.browser.modelutil.PropertyModel; import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.download.R; import org.chromium.chrome.download.R;
import org.chromium.components.offline_items_collection.OfflineItemVisuals; import org.chromium.components.offline_items_collection.OfflineItemVisuals;
/** A {@link RecyclerView.ViewHolder} specifically meant to display a generic {@code OfflineItem}. /** A {@link RecyclerView.ViewHolder} specifically meant to display a generic {@code OfflineItem}.
*/ */
public class GenericViewHolder extends ThumbnailAwareViewHolder { public class GenericViewHolder extends OfflineItemViewHolder {
private static final int INVALID_ID = -1;
private final TextView mTitle; private final TextView mTitle;
private final TextView mCaption; private final TextView mCaption;
private final AppCompatImageView mThumbnailView;
private Bitmap mThumbnailBitmap;
/** The icon to use when there is no thumbnail. */ private @DrawableRes int mGenericIconId;
private @DrawableRes int mIconId = INVALID_ID;
/** Creates a new {@link GenericViewHolder} instance. */ /** Creates a new {@link GenericViewHolder} instance. */
public static GenericViewHolder create(ViewGroup parent) { public static GenericViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()) View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.download_manager_generic_item, null); .inflate(R.layout.download_manager_generic_item, null);
int imageSize = parent.getContext().getResources().getDimensionPixelSize( return new GenericViewHolder(view);
R.dimen.download_manager_generic_thumbnail_size);
return new GenericViewHolder(view, imageSize);
} }
private GenericViewHolder(View view, int thumbnailSizePx) { private GenericViewHolder(View view) {
super(view, thumbnailSizePx, thumbnailSizePx); super(view);
mTitle = (TextView) itemView.findViewById(R.id.title); mTitle = itemView.findViewById(R.id.title);
mCaption = (TextView) itemView.findViewById(R.id.caption); mCaption = itemView.findViewById(R.id.caption);
mThumbnailView = (AppCompatImageView) itemView.findViewById(R.id.thumbnail);
mThumbnail.setForegroundScaleTypeCompat(ImageView.ScaleType.CENTER);
} }
// ListItemViewHolder implementation. // OfflineItemViewHolder implementation.
@Override @Override
public void bind(PropertyModel properties, ListItem item) { public void bind(PropertyModel properties, ListItem item) {
super.bind(properties, item); super.bind(properties, item);
...@@ -65,39 +57,43 @@ public class GenericViewHolder extends ThumbnailAwareViewHolder { ...@@ -65,39 +57,43 @@ public class GenericViewHolder extends ThumbnailAwareViewHolder {
mTitle.setText(offlineItem.item.title); mTitle.setText(offlineItem.item.title);
mCaption.setText(UiUtils.generateGenericCaption(offlineItem.item)); mCaption.setText(UiUtils.generateGenericCaption(offlineItem.item));
mIconId = UiUtils.getIconForItem(offlineItem.item); // Build invalid icon.
updateThumbnailView(); @DrawableRes
} int iconId = UiUtils.getIconForItem(offlineItem.item);
if (iconId != mGenericIconId) {
mGenericIconId = iconId;
@Override Drawable icon = DrawableCompat.wrap(
void onVisualsChanged(ImageView view, OfflineItemVisuals visuals) { ApiCompatibilityUtils.getDrawable(itemView.getResources(), mGenericIconId));
mThumbnailBitmap = visuals == null ? null : visuals.icon; DrawableCompat.setTintList(icon,
updateThumbnailView(); AppCompatResources.getColorStateList(
} itemView.getContext(), R.color.dark_mode_tint));
private void updateThumbnailView() { mThumbnail.setUnavailableDrawable(icon);
Resources resources = itemView.getContext().getResources(); mThumbnail.setWaitingDrawable(icon);
SelectionView selectionView = itemView.findViewById(R.id.selection); }
selectionView.setVisibility(selectionView.isSelected() ? View.VISIBLE : View.GONE);
mThumbnailView.setVisibility(selectionView.isSelected() ? View.GONE : View.VISIBLE); mSelectionView.setVisibility(mSelectionView.isSelected() ? View.VISIBLE : View.INVISIBLE);
if (mThumbnailBitmap != null) { mThumbnail.setVisibility(mSelectionView.isSelected() ? View.INVISIBLE : View.VISIBLE);
assert !mThumbnailBitmap.isRecycled(); }
mThumbnailView.setBackground(null); @Override
ImageViewCompat.setImageTintList(mThumbnailView, null); protected Drawable onThumbnailRetrieved(OfflineItemVisuals visuals) {
Resources resources = itemView.getResources();
boolean hasThumbnail = visuals != null && visuals.icon != null;
RoundedBitmapDrawable drawable = RoundedBitmapDrawable drawable = null;
RoundedBitmapDrawableFactory.create(resources, mThumbnailBitmap); if (hasThumbnail) {
drawable = RoundedBitmapDrawableFactory.create(resources, visuals.icon);
drawable.setCircular(true); drawable.setCircular(true);
mThumbnailView.setImageDrawable(drawable);
} else if (mIconId != INVALID_ID) { mThumbnail.setBackground(null);
mThumbnailView.setBackgroundResource(R.drawable.list_item_icon_modern_bg); } else {
mThumbnailView.getBackground().setLevel( mThumbnail.setBackgroundResource(R.drawable.list_item_icon_modern_bg);
mThumbnail.getBackground().setLevel(
resources.getInteger(R.integer.list_item_level_default)); resources.getInteger(R.integer.list_item_level_default));
mThumbnailView.setImageResource(mIconId);
ImageViewCompat.setImageTintList(mThumbnailView,
AppCompatResources.getColorStateList(
mThumbnailView.getContext(), R.color.dark_mode_tint));
} }
return drawable;
} }
} }
...@@ -4,26 +4,16 @@ ...@@ -4,26 +4,16 @@
package org.chromium.chrome.browser.download.home.list.holder; package org.chromium.chrome.browser.download.home.list.holder;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.chromium.chrome.browser.download.home.list.ListItem; import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.ListProperties;
import org.chromium.chrome.browser.download.home.list.view.AsyncImageView;
import org.chromium.chrome.browser.modelutil.PropertyModel; import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.download.R; import org.chromium.chrome.download.R;
import org.chromium.components.offline_items_collection.ContentId;
import org.chromium.components.offline_items_collection.OfflineItem;
/** A {@link RecyclerView.ViewHolder} specifically meant to display an image {@code OfflineItem}. */ /** A {@link RecyclerView.ViewHolder} specifically meant to display an image {@code OfflineItem}. */
public class ImageViewHolder extends ListItemViewHolder { public class ImageViewHolder extends OfflineItemViewHolder {
private final AsyncImageView mThumbnail;
private ContentId mBoundItemId;
public static ImageViewHolder create(ViewGroup parent) { public static ImageViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()) View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.download_manager_image_item, null); .inflate(R.layout.download_manager_image_item, null);
...@@ -32,30 +22,12 @@ public class ImageViewHolder extends ListItemViewHolder { ...@@ -32,30 +22,12 @@ public class ImageViewHolder extends ListItemViewHolder {
public ImageViewHolder(View view) { public ImageViewHolder(View view) {
super(view); super(view);
mThumbnail = itemView.findViewById(R.id.thumbnail);
} }
// ListItemViewHolder implementation. // ListItemViewHolder implementation.
@Override @Override
public void bind(PropertyModel properties, ListItem item) { public void bind(PropertyModel properties, ListItem item) {
OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item; super.bind(properties, item);
mThumbnail.setContentDescription(((ListItem.OfflineItemListItem) item).item.title);
mThumbnail.setContentDescription(offlineItem.title);
if (offlineItem.id.equals(mBoundItemId)) return;
mBoundItemId = offlineItem.id;
mThumbnail.setAsyncImageDrawable((consumer, width, height) -> {
return properties.get(ListProperties.PROVIDER_VISUALS)
.getVisuals(offlineItem, width, height, (id, visuals) -> {
if (!id.equals(mBoundItemId)) return;
Drawable drawable = null;
if (visuals != null && visuals.icon != null) {
drawable = new BitmapDrawable(itemView.getResources(), visuals.icon);
}
consumer.onResult(drawable);
});
});
} }
} }
// Copyright 2018 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.download.home.list.holder;
import android.support.v7.widget.AppCompatImageButton;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.ListProperties;
import org.chromium.chrome.browser.download.home.list.UiUtils;
import org.chromium.chrome.browser.download.home.list.view.AutoAnimatorDrawable;
import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.download.R;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItemState;
/**
* A {@link RecyclerView.ViewHolder} specifically meant to display an in-progress video {@code
* OfflineItem}.
*/
public class InProgressImageViewHolder extends ListItemViewHolder {
private final ImageView mPlaceholder;
private final TextView mCaption;
private final CircularProgressView mActionButton;
private final AppCompatImageButton mCancelButton;
/**
* Creates a new {@link InProgressViewHolder} instance.
*/
public static InProgressImageViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.download_manager_in_progress_image_item, null);
return new InProgressImageViewHolder(view);
}
/** Constructor. */
public InProgressImageViewHolder(View view) {
super(view);
mPlaceholder = view.findViewById(R.id.placeholder);
mCaption = view.findViewById(R.id.caption);
mActionButton = view.findViewById(R.id.action_button);
mCancelButton = view.findViewById(R.id.cancel_button);
mPlaceholder.setImageDrawable(AutoAnimatorDrawable.wrap(
org.chromium.chrome.browser.download.home.list.view.UiUtils.getDrawable(
view.getContext(), R.drawable.async_image_view_waiting)));
}
// ListItemViewHolder implementation.
@Override
public void bind(PropertyModel properties, ListItem item) {
OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
mPlaceholder.setContentDescription(offlineItem.title);
// TODO(shaktisahu): Create status string for the new specs.
mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.progress));
mCancelButton.setOnClickListener(
v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem));
UiUtils.setProgressForOfflineItem(mActionButton, offlineItem);
mActionButton.setOnClickListener(view -> {
switch (offlineItem.state) {
case OfflineItemState.IN_PROGRESS: // Intentional fallthrough.
case OfflineItemState.PENDING:
properties.get(ListProperties.CALLBACK_PAUSE).onResult(offlineItem);
break;
case OfflineItemState.INTERRUPTED: // Intentional fallthrough.
case OfflineItemState.PAUSED:
properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem);
break;
case OfflineItemState.COMPLETE: // Intentional fallthrough.
case OfflineItemState.CANCELLED: // Intentional fallthrough.
case OfflineItemState.FAILED:
// TODO(883387): Support retrying failed downloads.
properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem);
break;
}
});
}
}
...@@ -51,6 +51,8 @@ public abstract class ListItemViewHolder extends ViewHolder { ...@@ -51,6 +51,8 @@ public abstract class ListItemViewHolder extends ViewHolder {
return SeparatorViewHolder.create(parent, false); return SeparatorViewHolder.create(parent, false);
case ListUtils.ViewType.IN_PROGRESS_VIDEO: case ListUtils.ViewType.IN_PROGRESS_VIDEO:
return InProgressVideoViewHolder.create(parent); return InProgressVideoViewHolder.create(parent);
case ListUtils.ViewType.IN_PROGRESS_IMAGE:
return InProgressImageViewHolder.create(parent);
} }
assert false; assert false;
......
// Copyright 2018 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.download.home.list.holder;
import android.support.annotation.CallSuper;
import android.view.View;
import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.ListProperties;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.widget.ListMenuButton;
import org.chromium.chrome.download.R;
/**
* Helper {@link RecyclerView.ViewHolder} that handles showing a 3-dot menu with preset actions.
*/
class MoreButtonViewHolder extends ListItemViewHolder implements ListMenuButton.Delegate {
private final ListMenuButton mMore;
private Runnable mShareCallback;
private Runnable mDeleteCallback;
/**
* Creates a new instance of a {@link MoreButtonViewHolder}.
*/
public MoreButtonViewHolder(View view) {
super(view);
mMore = (ListMenuButton) view.findViewById(R.id.more);
if (mMore != null) mMore.setDelegate(this);
}
// ListItemViewHolder implementation.
@CallSuper
@Override
public void bind(PropertyModel properties, ListItem item) {
ListItem.OfflineItemListItem offlineItem = (ListItem.OfflineItemListItem) item;
mShareCallback =
() -> properties.get(ListProperties.CALLBACK_SHARE).onResult(offlineItem.item);
mDeleteCallback =
() -> properties.get(ListProperties.CALLBACK_REMOVE).onResult(offlineItem.item);
if (mMore != null) {
mMore.setClickable(!properties.get(ListProperties.SELECTION_MODE_ACTIVE));
}
}
// ListMenuButton.Delegate implementation.
@Override
public ListMenuButton.Item[] getItems() {
return new ListMenuButton.Item[] {
new ListMenuButton.Item(itemView.getContext(), R.string.share, true),
new ListMenuButton.Item(itemView.getContext(), R.string.delete, true)};
}
@Override
public void onItemSelected(ListMenuButton.Item item) {
if (item.getTextId() == R.string.share) {
if (mShareCallback != null) mShareCallback.run();
} else if (item.getTextId() == R.string.delete) {
if (mDeleteCallback != null) mDeleteCallback.run();
}
}
}
...@@ -7,19 +7,17 @@ package org.chromium.chrome.browser.download.home.list.holder; ...@@ -7,19 +7,17 @@ package org.chromium.chrome.browser.download.home.list.holder;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.chrome.browser.download.home.list.ListItem; import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.UiUtils; import org.chromium.chrome.browser.download.home.list.UiUtils;
import org.chromium.chrome.browser.modelutil.PropertyModel; import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.download.R; import org.chromium.chrome.download.R;
import org.chromium.components.offline_items_collection.OfflineItemVisuals;
/** /**
* A {@link RecyclerView.ViewHolder} specifically meant to display a prefetch item. * A {@link RecyclerView.ViewHolder} specifically meant to display a prefetch item.
*/ */
public class PrefetchViewHolder extends ThumbnailAwareViewHolder { public class PrefetchViewHolder extends OfflineItemViewHolder {
private final TextView mTitle; private final TextView mTitle;
private final TextView mCaption; private final TextView mCaption;
private final TextView mTimestamp; private final TextView mTimestamp;
...@@ -30,13 +28,11 @@ public class PrefetchViewHolder extends ThumbnailAwareViewHolder { ...@@ -30,13 +28,11 @@ public class PrefetchViewHolder extends ThumbnailAwareViewHolder {
public static PrefetchViewHolder create(ViewGroup parent) { public static PrefetchViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()) View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.download_manager_prefetch_item, null); .inflate(R.layout.download_manager_prefetch_item, null);
int imageSize = parent.getContext().getResources().getDimensionPixelSize( return new PrefetchViewHolder(view);
R.dimen.download_manager_prefetch_thumbnail_size);
return new PrefetchViewHolder(view, imageSize);
} }
private PrefetchViewHolder(View view, int thumbnailSizePx) { private PrefetchViewHolder(View view) {
super(view, thumbnailSizePx, thumbnailSizePx); super(view);
mTitle = (TextView) itemView.findViewById(R.id.title); mTitle = (TextView) itemView.findViewById(R.id.title);
mCaption = (TextView) itemView.findViewById(R.id.caption); mCaption = (TextView) itemView.findViewById(R.id.caption);
mTimestamp = (TextView) itemView.findViewById(R.id.timestamp); mTimestamp = (TextView) itemView.findViewById(R.id.timestamp);
...@@ -52,9 +48,4 @@ public class PrefetchViewHolder extends ThumbnailAwareViewHolder { ...@@ -52,9 +48,4 @@ public class PrefetchViewHolder extends ThumbnailAwareViewHolder {
mCaption.setText(UiUtils.generatePrefetchCaption(offlineItem.item)); mCaption.setText(UiUtils.generatePrefetchCaption(offlineItem.item));
mTimestamp.setText(UiUtils.generatePrefetchTimestamp(offlineItem.date)); mTimestamp.setText(UiUtils.generatePrefetchTimestamp(offlineItem.date));
} }
@Override
void onVisualsChanged(ImageView view, OfflineItemVisuals visuals) {
view.setImageBitmap(visuals == null ? null : visuals.icon);
}
} }
...@@ -4,31 +4,23 @@ ...@@ -4,31 +4,23 @@
package org.chromium.chrome.browser.download.home.list.holder; package org.chromium.chrome.browser.download.home.list.holder;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.chrome.browser.download.home.list.ListItem; import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.ListProperties;
import org.chromium.chrome.browser.download.home.list.UiUtils; import org.chromium.chrome.browser.download.home.list.UiUtils;
import org.chromium.chrome.browser.download.home.list.view.AsyncImageView;
import org.chromium.chrome.browser.modelutil.PropertyModel; import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.download.R; import org.chromium.chrome.download.R;
import org.chromium.components.offline_items_collection.ContentId;
import org.chromium.components.offline_items_collection.OfflineItem; import org.chromium.components.offline_items_collection.OfflineItem;
/** /**
* A {@link RecyclerView.ViewHolder} specifically meant to display a video {@code OfflineItem}. * A {@link RecyclerView.ViewHolder} specifically meant to display a video {@code OfflineItem}.
*/ */
public class VideoViewHolder extends MoreButtonViewHolder { public class VideoViewHolder extends OfflineItemViewHolder {
private final TextView mTitle; private final TextView mTitle;
private final TextView mCaption; private final TextView mCaption;
private final AsyncImageView mThumbnail;
private ContentId mBoundItemId;
/** /**
* Creates a new {@link VideoViewHolder} instance. * Creates a new {@link VideoViewHolder} instance.
...@@ -45,34 +37,16 @@ public class VideoViewHolder extends MoreButtonViewHolder { ...@@ -45,34 +37,16 @@ public class VideoViewHolder extends MoreButtonViewHolder {
mTitle = itemView.findViewById(R.id.title); mTitle = itemView.findViewById(R.id.title);
mCaption = itemView.findViewById(R.id.caption); mCaption = itemView.findViewById(R.id.caption);
mThumbnail = itemView.findViewById(R.id.thumbnail);
} }
// MoreButtonViewHolder implementation. // MoreButtonViewHolder implementation.
@Override @Override
public void bind(PropertyModel properties, ListItem item) { public void bind(PropertyModel properties, ListItem item) {
super.bind(properties, item); super.bind(properties, item);
OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item; OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
mTitle.setText(offlineItem.title); mTitle.setText(offlineItem.title);
mCaption.setText(UiUtils.generateGenericCaption(offlineItem)); mCaption.setText(UiUtils.generateGenericCaption(offlineItem));
mThumbnail.setContentDescription(offlineItem.title); mThumbnail.setContentDescription(offlineItem.title);
if (offlineItem.id.equals(mBoundItemId)) return;
mBoundItemId = offlineItem.id;
mThumbnail.setAsyncImageDrawable((consumer, width, height) -> {
return properties.get(ListProperties.PROVIDER_VISUALS)
.getVisuals(offlineItem, width, height, (id, visuals) -> {
if (!id.equals(mBoundItemId)) return;
Drawable drawable = null;
if (visuals != null && visuals.icon != null) {
drawable = new BitmapDrawable(itemView.getResources(), visuals.icon);
}
consumer.onResult(drawable);
});
});
} }
} }
// Copyright 2018 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.download.home.list.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import org.chromium.chrome.download.R;
/**
* Helper FrameLayout that knows how to make children fit certain aspect ratios.
* TODO(dtrainor): Remove this once we get proper support for constraint layout.
*
* Below is an example usage to make an ImageView a square based on the width. Note that you do not
* need to set both layout_width and layout_height if setting layout_aspectRatio.
*
* <org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:app="http://schemas.android.com/apk/res-auto"
* android:layout_width="match_parent"
* android:layout_height="wrap_content">
* <ImageView
* android:layout_width="match_parent"
* app:layout_aspectRatio="100%" />
* </org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout>
*/
public class AspectRatioFrameLayout extends FrameLayout {
/** Creates an instance of {@link AspectRatioFrameLayout}. */
public AspectRatioFrameLayout(Context context) {
this(context, null);
}
/** Creates an instance of {@link AspectRatioFrameLayout}. */
public AspectRatioFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/** Creates an instance of {@link AspectRatioFrameLayout}. */
public AspectRatioFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// FrameLayout implementation.
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
@SuppressWarnings("DrawAllocation")
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width =
View.MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
int height =
View.MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (!(view.getLayoutParams() instanceof LayoutParams)) continue;
LayoutParams params = (LayoutParams) view.getLayoutParams();
params.mRestorableWidth = params.width;
params.mRestorableHeight = params.height;
float aspectRatio = params.aspectRatio;
if (aspectRatio <= 0.f) continue;
boolean widthMovable = params.mOverrodeWidth || params.mRestorableWidth == 0;
boolean heightMovable = params.mOverrodeHeight || params.mRestorableHeight == 0;
if (widthMovable) {
int childHeight =
params.height == LayoutParams.MATCH_PARENT ? height : params.height;
params.width = Math.round(childHeight * aspectRatio);
params.mOverrodeWidth = true;
}
if (heightMovable) {
int childWidth = params.width == LayoutParams.MATCH_PARENT ? width : params.width;
params.height = Math.round(childWidth / aspectRatio);
params.mOverrodeHeight = true;
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (!(view.getLayoutParams() instanceof LayoutParams)) continue;
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (params.mOverrodeWidth) params.width = params.mRestorableWidth;
if (params.mOverrodeHeight) params.height = params.mRestorableHeight;
params.mOverrodeWidth = false;
params.mOverrodeHeight = false;
}
}
/**
* A set of layout parameters for {@link AspectRatioFrameLayout}.
*/
public static class LayoutParams extends FrameLayout.LayoutParams {
/** The aspect ratio to use and enforce. */
public float aspectRatio;
// Restorable parameters.
private boolean mOverrodeWidth;
private boolean mOverrodeHeight;
private int mRestorableWidth;
private int mRestorableHeight;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array = context.obtainStyledAttributes(
attrs, R.styleable.AspectRatioFrameLayout_Layout);
aspectRatio = array.getFraction(
R.styleable.AspectRatioFrameLayout_Layout_layout_aspectRatio, 1, 1, 0.f);
array.recycle();
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
public LayoutParams(FrameLayout.LayoutParams source) {
super((MarginLayoutParams) source);
gravity = source.gravity;
}
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
// Do not throw errors if width or height aren't supplied.
width = a.getLayoutDimension(widthAttr, 0);
height = a.getLayoutDimension(heightAttr, 0);
}
}
}
\ No newline at end of file
...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.download.home.list.view; ...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.download.home.list.view;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import org.chromium.base.Callback; import org.chromium.base.Callback;
...@@ -36,14 +37,16 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView { ...@@ -36,14 +37,16 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView {
Runnable get(Callback<Drawable> consumer, int widthPx, int heightPx); Runnable get(Callback<Drawable> consumer, int widthPx, int heightPx);
} }
private final Drawable mUnavailableDrawable; private Drawable mUnavailableDrawable;
private final Drawable mWaitingDrawable; private Drawable mWaitingDrawable;
private Factory mFactory; private Factory mFactory;
private Runnable mCancelable; private Runnable mCancelable;
private boolean mWaitingForResponse; private boolean mWaitingForResponse;
private @Nullable Object mIdentifier;
/** Creates an {@link AsyncImageDrawable instance. */ /** Creates an {@link AsyncImageDrawable instance. */
public AsyncImageView(Context context) { public AsyncImageView(Context context) {
this(context, null, 0); this(context, null, 0);
...@@ -74,9 +77,16 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView { ...@@ -74,9 +77,16 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView {
* Starts loading a {@link Drawable} from {@code factory}. This will automatically clear out * Starts loading a {@link Drawable} from {@code factory}. This will automatically clear out
* any outstanding request state and start a new one. * any outstanding request state and start a new one.
* *
* @param factory The {@link Factory} to use that will provide the {@link Drawable}. * @param factory The {@link Factory} to use that will provide the {@link Drawable}.
* @param identifier An identification for this particular request. Subsequent calls with the
* same {@link Object} will be ignored until either {@code null} or a
* different {@link Object} are passed in. This lets us ignore redundant
* calls.
*/ */
public void setAsyncImageDrawable(Factory factory) { public void setAsyncImageDrawable(Factory factory, @Nullable Object identifier) {
if (mIdentifier != null && identifier != null && mIdentifier.equals(identifier)) return;
mIdentifier = identifier;
// This will clear out any outstanding request. // This will clear out any outstanding request.
setImageDrawable(null); setImageDrawable(null);
setForegroundDrawableCompat(mWaitingDrawable); setForegroundDrawableCompat(mWaitingDrawable);
...@@ -85,6 +95,26 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView { ...@@ -85,6 +95,26 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView {
retrieveDrawableIfNeeded(); retrieveDrawableIfNeeded();
} }
/**
* @param unavailableDrawable Sets the {@link Drawable} to use when there is no thumbnail
* available.
*/
public void setUnavailableDrawable(Drawable unavailableDrawable) {
boolean showUnavailable =
getForegroundDrawableCompat() == mUnavailableDrawable && !mWaitingForResponse;
mUnavailableDrawable = AutoAnimatorDrawable.wrap(unavailableDrawable);
if (showUnavailable) setForegroundDrawableCompat(mUnavailableDrawable);
}
/**
* @param waitingDrawable Sets the {@link Drawable} to use when waiting for an outstanding
* asynchronous thumbnail request.
*/
public void setWaitingDrawable(Drawable waitingDrawable) {
mWaitingDrawable = AutoAnimatorDrawable.wrap(waitingDrawable);
if (mWaitingForResponse) setForegroundDrawableCompat(mWaitingDrawable);
}
// RoundedCornerImageView implementation. // RoundedCornerImageView implementation.
@Override @Override
public void setImageDrawable(Drawable drawable) { public void setImageDrawable(Drawable drawable) {
...@@ -104,7 +134,12 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView { ...@@ -104,7 +134,12 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView {
retrieveDrawableIfNeeded(); retrieveDrawableIfNeeded();
} }
private void setAsyncImageDrawableResponse(Drawable drawable) { private void setAsyncImageDrawableResponse(Drawable drawable, Object identifier) {
// If we ended up swapping out the identifier and somehow this request didn't cancel ignore
// the response. This does a direct == comparison instead of .equals() because any new
// request should have canceled this one (we'll leave null alone though).
if (mIdentifier != identifier) return;
mCancelable = null; mCancelable = null;
mWaitingForResponse = false; mWaitingForResponse = false;
setForegroundDrawableCompat(drawable == null ? mUnavailableDrawable : null); setForegroundDrawableCompat(drawable == null ? mUnavailableDrawable : null);
...@@ -129,8 +164,11 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView { ...@@ -129,8 +164,11 @@ public class AsyncImageView extends ForegroundRoundedCornerImageView {
if (mFactory != null) { if (mFactory != null) {
// Start to retrieve the drawable. // Start to retrieve the drawable.
mWaitingForResponse = true; mWaitingForResponse = true;
mCancelable =
mFactory.get(this ::setAsyncImageDrawableResponse, getWidth(), getHeight()); Object localIdentifier = mIdentifier;
mCancelable = mFactory.get(d
-> setAsyncImageDrawableResponse(d, localIdentifier),
getWidth(), getHeight());
// If setAsyncImageDrawableResponse is called synchronously, clear mCancelable. // If setAsyncImageDrawableResponse is called synchronously, clear mCancelable.
if (!mWaitingForResponse) mCancelable = null; if (!mWaitingForResponse) mCancelable = null;
......
...@@ -103,8 +103,10 @@ public class CircularProgressView extends AppCompatImageButton { ...@@ -103,8 +103,10 @@ public class CircularProgressView extends AppCompatImageButton {
if (progress == INDETERMINATE) { if (progress == INDETERMINATE) {
mForegroundHelper.setDrawable(mIndeterminateProgress); mForegroundHelper.setDrawable(mIndeterminateProgress);
} else { } else {
progress = MathUtils.clamp(progress, 0, 100); if (mDeterminateProgress != null) {
mDeterminateProgress.setLevel(progress * MAX_LEVEL / 100); progress = MathUtils.clamp(progress, 0, 100);
mDeterminateProgress.setLevel(progress * MAX_LEVEL / 100);
}
mForegroundHelper.setDrawable(mDeterminateProgress); mForegroundHelper.setDrawable(mDeterminateProgress);
} }
} }
......
...@@ -80,8 +80,6 @@ public class ForegroundDrawableCompat ...@@ -80,8 +80,6 @@ public class ForegroundDrawableCompat
private boolean mOnBoundsChanged; private boolean mOnBoundsChanged;
private Drawable mDrawable; private Drawable mDrawable;
// TODO(dtrainor): Add support for more scale types.
// Right now the only two supported types are FIT_* tyes.
private ImageView.ScaleType mScaleType = ImageView.ScaleType.FIT_CENTER; private ImageView.ScaleType mScaleType = ImageView.ScaleType.FIT_CENTER;
/** /**
...@@ -137,6 +135,10 @@ public class ForegroundDrawableCompat ...@@ -137,6 +135,10 @@ public class ForegroundDrawableCompat
/** /**
* Determines how the foreground {@code Drawable} will be drawn in front of the {@link View}. * Determines how the foreground {@code Drawable} will be drawn in front of the {@link View}.
* Right now the only supported types are FIT_* types and the CENTER type.
*
* TODO(dtrainor): Add support for more scale types.
*
* @param type The type of scale to apply to the {@Drawable} (see {@link ImageView.ScaleType}). * @param type The type of scale to apply to the {@Drawable} (see {@link ImageView.ScaleType}).
*/ */
public void setScaleType(ImageView.ScaleType type) { public void setScaleType(ImageView.ScaleType type) {
...@@ -242,6 +244,10 @@ public class ForegroundDrawableCompat ...@@ -242,6 +244,10 @@ public class ForegroundDrawableCompat
} else if (mScaleType == ImageView.ScaleType.FIT_END) { } else if (mScaleType == ImageView.ScaleType.FIT_END) {
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END); mDrawMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
mDrawable.setBounds(0, 0, drawableWidth, drawableHeight); mDrawable.setBounds(0, 0, drawableWidth, drawableHeight);
} else if (mScaleType == ImageView.ScaleType.CENTER) {
mDrawMatrix.setTranslate(Math.round((viewWidth - drawableWidth) * 0.5f),
Math.round((viewHeight - drawableHeight) * 0.5f));
mDrawable.setBounds(0, 0, drawableWidth, drawableHeight);
} else { } else {
mDrawable.setBounds(0, 0, viewWidth, viewHeight); mDrawable.setBounds(0, 0, viewWidth, viewHeight);
} }
......
...@@ -10,6 +10,7 @@ import android.graphics.Canvas; ...@@ -10,6 +10,7 @@ import android.graphics.Canvas;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import org.chromium.chrome.download.R; import org.chromium.chrome.download.R;
import org.chromium.ui.widget.RoundedCornerImageView; import org.chromium.ui.widget.RoundedCornerImageView;
...@@ -50,6 +51,19 @@ public class ForegroundRoundedCornerImageView extends RoundedCornerImageView { ...@@ -50,6 +51,19 @@ public class ForegroundRoundedCornerImageView extends RoundedCornerImageView {
mForegroundHelper.setDrawable(drawable); mForegroundHelper.setDrawable(drawable);
} }
/** @return The current foreground {@link Drawable}. */
public Drawable getForegroundDrawableCompat() {
return mForegroundHelper.getDrawable();
}
/**
* @param scaleType Sets the {@link ImageView.ScaleType} to use for the foreground
* {@link Drawable}.
*/
public void setForegroundScaleTypeCompat(ImageView.ScaleType scaleType) {
mForegroundHelper.setScaleType(scaleType);
}
// RoundedCornerImageView implementation. // RoundedCornerImageView implementation.
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
......
// Copyright 2018 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.download.home.list.view;
import android.content.Context;
import android.util.AttributeSet;
/**
* Helper {@link AsyncImageView} that will force the dimensions of the view to be equal.
*/
public class SquareAsyncImageView extends AsyncImageView {
/** Creates an instance of {@link SquareAsyncImageView}. */
public SquareAsyncImageView(Context context) {
this(context, null, 0);
}
/** Creates an instance of {@link SquareAsyncImageView}. */
public SquareAsyncImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/** Creates an instance of {@link SquareAsyncImageView}. */
public SquareAsyncImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// AsyncImageView implementation.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int squareSize;
// Check if there is a valid explicitly set dimension first (prioritize width).
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && measureWidth > 0) {
squareSize = measureWidth;
} else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
&& measureHeight > 0) {
squareSize = measureHeight;
} else {
squareSize = Math.min(measureWidth, measureHeight);
}
int squareMeasureSpec = MeasureSpec.makeMeasureSpec(squareSize, MeasureSpec.EXACTLY);
super.onMeasure(squareMeasureSpec, squareMeasureSpec);
}
}
\ No newline at end of file
...@@ -517,21 +517,21 @@ chrome_java_sources = [ ...@@ -517,21 +517,21 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/download/home/list/holder/DateViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/DateViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressImageViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/MoreButtonViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/PrefetchViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/PrefetchViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/SectionTitleViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/SectionTitleViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/SeparatorViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/SeparatorViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/ThumbnailAwareViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java", "java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/AspectRatioFrameLayout.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/AsyncImageView.java", "java/src/org/chromium/chrome/browser/download/home/list/view/AsyncImageView.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/AutoAnimatorDrawable.java", "java/src/org/chromium/chrome/browser/download/home/list/view/AutoAnimatorDrawable.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/CircularProgressView.java", "java/src/org/chromium/chrome/browser/download/home/list/view/CircularProgressView.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundDrawableCompat.java", "java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundDrawableCompat.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundRoundedCornerImageView.java", "java/src/org/chromium/chrome/browser/download/home/list/view/ForegroundRoundedCornerImageView.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/SquareAsyncImageView.java",
"java/src/org/chromium/chrome/browser/download/home/list/view/UiUtils.java", "java/src/org/chromium/chrome/browser/download/home/list/view/UiUtils.java",
"java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java", "java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java",
"java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java", "java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment