Commit 20ae78ee authored by David Trainor's avatar David Trainor Committed by Commit Bot

Add a styleable circular progress bar for DHv2

Add a new CircularProgressView to be used in various places in downloads
home.  This currently supports styling and the initial small/large
progress bar style buttons that we need.

BUG=868554

Change-Id: I68aaea3984e5cd3ff62b1ec6a298a409f5d00b08
Reviewed-on: https://chromium-review.googlesource.com/1220177
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@{#591362}
parent fcc0363c
<?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="?attr/selectableItemBackground" />
<item>
<shape
android:shape="oval">
<solid android:color="@android:color/white" />
<size android:width="48dp"
android:height="48dp" />
</shape>
</item>
</layer-list>
\ No newline at end of file
...@@ -4,10 +4,13 @@ ...@@ -4,10 +4,13 @@
found in the LICENSE file. --> found in the LICENSE file. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/background"> <item android:drawable="?attr/selectableItemBackground" />
<item>
<shape <shape
android:shape="oval"> android:shape="oval">
<solid android:color="@color/modern_grey_300" /> <solid android:color="@color/modern_grey_300" />
<size android:width="36dp"
android:height="36dp" />
</shape> </shape>
</item> </item>
</layer-list> </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:id="@android:id/progress">
<rotate
android:fromDegrees="270"
android:toDegrees="270" >
<shape
android:innerRadius="28dp"
android:thickness="2dp"
android:useLevel="true"
android:shape="ring">
<gradient
android:startColor="@color/modern_grey_900"
android:endColor="@color/modern_grey_900"
android:type="sweep" />
</shape>
</rotate>
</item>
</layer-list>
\ No newline at end of file
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/progress"> <item android:id="@android:id/progress">
<rotate <rotate
android:fromDegrees="180" android:fromDegrees="270"
android:toDegrees="180" > android:toDegrees="270" >
<shape <shape
android:innerRadius="16dp" android:innerRadius="16dp"
android:thickness="2dp" android:thickness="2dp"
......
<?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. -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:aapt="http://schemas.android.com/aapt"
tools:targetApi="21"
tools:ignore="UnusedResources">
<aapt:attr name="android:drawable">
<vector
android:width="60dp"
android:height="60dp"
android:viewportWidth="60"
android:viewportHeight="60"
android:tint="@color/modern_grey_900">
<group android:name="container"
android:pivotX="30"
android:pivotY="30">
<path android:name="arc"
android:fillColor="@android:color/transparent"
android:strokeColor="@color/modern_grey_900"
android:strokeLineCap="square"
android:strokeLineJoin="miter"
android:strokeWidth="2"
android:trimPathStart="0"
android:trimPathEnd="0.05"
android:trimPathOffset="0"
android:pathData="m 30,1 a 29,29 0 1,1 0,58 a 29,29 0 1,1 0,-58" />
</group>
</vector>
</aapt:attr>
<target android:name="arc">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="1333"
android:propertyName="trimPathStart"
android:repeatCount="-1"
android:valueFrom="0"
android:valueTo="0.75"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator
android:pathData="L0.5,0 C 0.7,0 0.6,1 1,1"/>
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="1333"
android:propertyName="trimPathEnd"
android:repeatCount="-1"
android:valueFrom="0.05"
android:valueTo="0.8"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator
android:pathData="C0.2,0 0.1,1 0.5, 1 L 1,1"/>
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="1333"
android:propertyName="trimPathOffset"
android:repeatCount="-1"
android:valueFrom="0"
android:valueTo="0.25"
android:valueType="floatType"
android:interpolator="@android:anim/linear_interpolator" />
</set>
</aapt:attr>
</target>
<target android:name="container">
<aapt:attr name="android:animation">
<objectAnimator
android:duration="4444"
android:interpolator="@android:anim/linear_interpolator"
android:propertyName="rotation"
android:repeatCount="-1"
android:valueFrom="0"
android:valueTo="720"
android:valueType="floatType"/>
</aapt:attr>
</target>
</animated-vector>
\ 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. -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:aapt="http://schemas.android.com/aapt"
tools:targetApi="21"
tools:ignore="UnusedResources">
<aapt:attr name="android:drawable">
<vector
android:width="36dp"
android:height="36dp"
android:viewportWidth="36"
android:viewportHeight="36"
android:tint="@color/modern_grey_800">
<group android:name="container"
android:pivotX="18"
android:pivotY="18">
<path android:name="arc"
android:fillColor="@android:color/transparent"
android:strokeColor="@color/modern_grey_800"
android:strokeLineCap="square"
android:strokeLineJoin="miter"
android:strokeWidth="2"
android:trimPathStart="0"
android:trimPathEnd="0.05"
android:trimPathOffset="0"
android:pathData="m 18,1 a 17,17 0 1,1 0,34 a 17,17 0 1,1 0,-34" />
</group>
</vector>
</aapt:attr>
<target android:name="arc">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="1333"
android:propertyName="trimPathStart"
android:repeatCount="-1"
android:valueFrom="0"
android:valueTo="0.75"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator
android:pathData="L0.5,0 C 0.7,0 0.6,1 1,1"/>
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="1333"
android:propertyName="trimPathEnd"
android:repeatCount="-1"
android:valueFrom="0.05"
android:valueTo="0.8"
android:valueType="floatType">
<aapt:attr name="android:interpolator">
<pathInterpolator
android:pathData="C0.2,0 0.1,1 0.5, 1 L 1,1"/>
</aapt:attr>
</objectAnimator>
<objectAnimator
android:duration="1333"
android:propertyName="trimPathOffset"
android:repeatCount="-1"
android:valueFrom="0"
android:valueTo="0.25"
android:valueType="floatType"
android:interpolator="@android:anim/linear_interpolator" />
</set>
</aapt:attr>
</target>
<target android:name="container">
<aapt:attr name="android:animation">
<objectAnimator
android:duration="4444"
android:interpolator="@android:anim/linear_interpolator"
android:propertyName="rotation"
android:repeatCount="-1"
android:valueFrom="0"
android:valueTo="720"
android:valueType="floatType"/>
</aapt:attr>
</target>
</animated-vector>
\ No newline at end of file
...@@ -16,44 +16,15 @@ ...@@ -16,44 +16,15 @@
app:columnCount="3" app:columnCount="3"
app:rowCount="2"> app:rowCount="2">
<ImageView <org.chromium.chrome.browser.download.home.list.view.CircularProgressView
android:layout_width="36dp" android:id="@+id/action_button"
android:layout_height="36dp"
app:layout_gravity="center"
app:layout_column="0"
app:layout_row="0"
app:layout_rowSpan="2"
tools:ignore="ContentDescription"
android:background="@drawable/download_circular_progress_bar_background"
android:layout_centerInParent="true"/>
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
app:layout_gravity="center"
style="?android:attr/progressBarStyleHorizontal"
app:layout_column="0" app:layout_column="0"
app:layout_row="0" app:layout_row="0"
app:layout_rowSpan="2" app:layout_rowSpan="2"
android:max="100" app:layout_gravity="center_vertical"
android:progress="0" style="@style/SmallCircularProgress" />
android:progressDrawable="@drawable/download_circular_progress_bar"/>
<org.chromium.chrome.browser.widget.TintedImageButton
android:id="@+id/pause_button"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_gravity="center"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/download_notification_pause_button"
android:src="@drawable/ic_pause_white_24dp"
app:chrometint="@color/default_icon_color"
app:layout_column="0"
app:layout_row="0"
app:layout_rowSpan="2"/>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
......
<?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.
-->
<android.support.v7.widget.GridLayout
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="@drawable/hairline_border_card_background"
app:columnCount="2"
app:rowCount="3">
<org.chromium.ui.widget.RoundedCornerImageView
android:id="@+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerInside"
android:adjustViewBounds="true"
android:src="@color/modern_grey_300"
app:layout_column="0"
app:layout_row="0"
app:layout_columnSpan="2"
app:layout_gravity="center"
app:cornerRadiusTopStart="@dimen/download_manager_prefetch_thumbnail_corner_radius"
app:cornerRadiusTopEnd="@dimen/download_manager_prefetch_thumbnail_corner_radius"/>
<org.chromium.chrome.browser.download.home.list.view.CircularProgressView
android:id="@+id/action_button"
app:layout_column="0"
app:layout_row="0"
app:layout_columnSpan="2"
app:layout_gravity="center"
style="@style/LargeCircularProgress" />
<TextView
android:id="@+id/title"
style="@style/DownloadItemText"
android:layout_width="wrap_content"
android:layout_marginTop="11dp"
android:textAppearance="@style/BlackTitle1"
app:layout_column="0"
app:layout_row="1"
android:layout_marginStart="16dp"
app:layout_gravity="fill_horizontal" />
<TextView
android:id="@+id/caption"
style="@style/DownloadItemText"
android:layout_width="wrap_content"
android:layout_marginBottom="11dp"
android:textAppearance="@style/BlackHint2"
app:layout_column="0"
app:layout_row="2"
android:layout_marginStart="16dp"
app:layout_gravity="fill_horizontal" />
<org.chromium.chrome.browser.widget.TintedImageButton
android:id="@+id/cancel_button"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_column="1"
app:layout_row="1"
app:layout_rowSpan="2"
app:layout_gravity="center_vertical|end"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/download_notification_cancel_button"
android:src="@drawable/btn_close"
app:chrometint="@color/default_icon_color" />
</android.support.v7.widget.GridLayout>
...@@ -18,11 +18,10 @@ ...@@ -18,11 +18,10 @@
<org.chromium.ui.widget.RoundedCornerImageView <org.chromium.ui.widget.RoundedCornerImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="200dp"
android:scaleType="centerCrop" android:scaleType="centerInside"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@color/modern_grey_300" android:src="@color/modern_grey_300"
android:src="@drawable/audio_playing_square"
app:layout_column="0" app:layout_column="0"
app:layout_row="0" app:layout_row="0"
app:layout_columnSpan="2" app:layout_columnSpan="2"
......
<?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. -->
<resources>
<declare-styleable name="CircularProgressView">
<attr name="indeterminateProgress" format="reference" />
<attr name="determinateProgress" format="reference" />
<attr name="resumeSrc" format="reference" />
<attr name="pauseSrc" format="reference" />
<attr name="retrySrc" format="reference" />
</declare-styleable>
</resources>
\ No newline at end of file
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Download Home --> <!-- Download Home -->
<style name="DownloadHomeStatusText"> <style name="DownloadHomeStatusText">
<item name="android:layout_width">wrap_content</item> <item name="android:layout_width">wrap_content</item>
...@@ -71,4 +72,28 @@ ...@@ -71,4 +72,28 @@
<item name="android:layout_marginStart">8dp</item> <item name="android:layout_marginStart">8dp</item>
<item name="android:layout_marginTop">8dp</item> <item name="android:layout_marginTop">8dp</item>
</style> </style>
<style name="SmallCircularProgress">
<item name="android:layout_width">36dp</item>
<item name="android:layout_height">36dp</item>
<item name="android:tint">@color/default_icon_color</item>
<item name="android:background">@drawable/circular_progress_bar_background_small</item>
<item name="indeterminateProgress">@drawable/circular_progress_bar_indeterminate_small</item>
<item name="determinateProgress">@drawable/circular_progress_bar_determinate_small</item>
<item name="resumeSrc">@drawable/ic_play_arrow_white_24dp</item>
<!-- TODO(883387): Support retrying failed downloads. -->
<item name="retrySrc">@drawable/ic_play_arrow_white_24dp</item>
<item name="pauseSrc">@drawable/ic_pause_white_24dp</item>
</style>
<style name="LargeCircularProgress">
<item name="android:layout_width">60dp</item>
<item name="android:layout_height">60dp</item>
<item name="android:tint">@color/default_icon_color</item>
<item name="android:background">@drawable/circular_progress_bar_background_large</item>
<item name="indeterminateProgress">@drawable/circular_progress_bar_indeterminate_large</item>
<item name="determinateProgress">@drawable/circular_progress_bar_determinate_large</item>
<item name="resumeSrc">@drawable/ic_play_arrow_white_36dp</item>
<!-- TODO(883387): Support retrying failed downloads. -->
<item name="retrySrc">@drawable/ic_play_arrow_white_36dp</item>
<item name="pauseSrc">@drawable/ic_pause_white_36dp</item>
</style>
</resources> </resources>
...@@ -150,7 +150,8 @@ class DateOrderedListView { ...@@ -150,7 +150,8 @@ class DateOrderedListView {
outRect.top = mImagePaddingPx; outRect.top = mImagePaddingPx;
outRect.bottom = mImagePaddingPx; outRect.bottom = mImagePaddingPx;
break; break;
case ListUtils.ViewType.VIDEO: case ListUtils.ViewType.VIDEO: // Intentional fallthrough.
case ListUtils.ViewType.IN_PROGRESS_VIDEO:
outRect.left = mPrefetchHorizontalPaddingPx; outRect.left = mPrefetchHorizontalPaddingPx;
outRect.right = mPrefetchHorizontalPaddingPx; outRect.right = mPrefetchHorizontalPaddingPx;
outRect.bottom = mPrefetchHorizontalPaddingPx; outRect.bottom = mPrefetchHorizontalPaddingPx;
......
...@@ -26,7 +26,7 @@ public class ListUtils { ...@@ -26,7 +26,7 @@ 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.SEPARATOR_DATE, ViewType.SEPARATOR_SECTION, ViewType.IN_PROGRESS_VIDEO})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface ViewType { public @interface ViewType {
int DATE = 0; int DATE = 0;
...@@ -39,6 +39,7 @@ public class ListUtils { ...@@ -39,6 +39,7 @@ public class ListUtils {
int SECTION_HEADER = 7; int SECTION_HEADER = 7;
int SEPARATOR_DATE = 8; int SEPARATOR_DATE = 8;
int SEPARATOR_SECTION = 9; int SEPARATOR_SECTION = 9;
int IN_PROGRESS_VIDEO = 10;
} }
/** 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. */
...@@ -72,25 +73,24 @@ public class ListUtils { ...@@ -72,25 +73,24 @@ public class ListUtils {
OfflineItemListItem offlineItem = (OfflineItemListItem) item; OfflineItemListItem offlineItem = (OfflineItemListItem) item;
if (offlineItem.item.isSuggested) return ViewType.PREFETCH; if (offlineItem.item.isSuggested) return ViewType.PREFETCH;
if (offlineItem.item.state == OfflineItemState.IN_PROGRESS
boolean inProgress = offlineItem.item.state == OfflineItemState.IN_PROGRESS
|| offlineItem.item.state == OfflineItemState.PAUSED || offlineItem.item.state == OfflineItemState.PAUSED
|| offlineItem.item.state == OfflineItemState.INTERRUPTED || offlineItem.item.state == OfflineItemState.INTERRUPTED
|| offlineItem.item.state == OfflineItemState.PENDING || offlineItem.item.state == OfflineItemState.PENDING
|| offlineItem.item.state == OfflineItemState.FAILED) { || offlineItem.item.state == OfflineItemState.FAILED;
return ViewType.IN_PROGRESS;
}
switch (offlineItem.item.filter) { switch (offlineItem.item.filter) {
case OfflineItemFilter.FILTER_VIDEO: case OfflineItemFilter.FILTER_VIDEO:
return ViewType.VIDEO; return inProgress ? ViewType.IN_PROGRESS_VIDEO : ViewType.VIDEO;
case OfflineItemFilter.FILTER_IMAGE: case OfflineItemFilter.FILTER_IMAGE:
return ViewType.IMAGE; return inProgress ? ViewType.IN_PROGRESS : 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:
// case OfflineItemFilter.FILTER_DOCUMENT: // case OfflineItemFilter.FILTER_DOCUMENT:
default: default:
return ViewType.GENERIC; return inProgress ? ViewType.IN_PROGRESS : ViewType.GENERIC;
} }
} else { } else {
return ViewType.DATE; return ViewType.DATE;
......
...@@ -13,9 +13,13 @@ import org.chromium.base.ContextUtils; ...@@ -13,9 +13,13 @@ import org.chromium.base.ContextUtils;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.download.DownloadUtils; import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.download.home.filter.Filters; import org.chromium.chrome.browser.download.home.filter.Filters;
import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
import org.chromium.chrome.browser.download.home.list.view.CircularProgressView.UiState;
import org.chromium.chrome.browser.util.MathUtils; import org.chromium.chrome.browser.util.MathUtils;
import org.chromium.components.offline_items_collection.OfflineItem; import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItem.Progress;
import org.chromium.components.offline_items_collection.OfflineItemFilter; import org.chromium.components.offline_items_collection.OfflineItemFilter;
import org.chromium.components.offline_items_collection.OfflineItemState;
import org.chromium.components.url_formatter.UrlFormatter; import org.chromium.components.url_formatter.UrlFormatter;
import java.util.Calendar; import java.util.Calendar;
...@@ -126,4 +130,67 @@ public final class UiUtils { ...@@ -126,4 +130,67 @@ public final class UiUtils {
return DownloadUtils.getIconResId(Filters.offlineItemFilterToDownloadFilter(item.filter), return DownloadUtils.getIconResId(Filters.offlineItemFilterToDownloadFilter(item.filter),
DownloadUtils.IconSize.DP_24); DownloadUtils.IconSize.DP_24);
} }
/**
* Populates a {@link CircularProgressView} based on the contents of an {@link OfflineItem}.
* This is a helper glue method meant to consolidate the setting of {@link CircularProgressView}
* state.
* @param view The {@link CircularProgressView} to update.
* @param item The {@link OfflineItem} to use as the source of the update state.
*/
public static void setProgressForOfflineItem(CircularProgressView view, OfflineItem item) {
Progress progress = item.progress;
final boolean indeterminate = progress != null && progress.isIndeterminate();
final int determinateProgress = progress != null ? progress.getPercentage() : 0;
final int activeProgress =
indeterminate ? CircularProgressView.INDETERMINATE : determinateProgress;
final int inactiveProgress = indeterminate ? 0 : determinateProgress;
@UiState
int shownState;
int shownProgress;
switch (item.state) {
case OfflineItemState.PENDING: // Intentional fallthrough.
case OfflineItemState.IN_PROGRESS:
shownState = CircularProgressView.UiState.RUNNING;
break;
case OfflineItemState.FAILED: // Intentional fallthrough.
case OfflineItemState.CANCELLED:
shownState = CircularProgressView.UiState.RETRY;
break;
case OfflineItemState.PAUSED:
case OfflineItemState.INTERRUPTED:
shownState = CircularProgressView.UiState.PAUSED;
break;
case OfflineItemState.COMPLETE: // Intentional fallthrough.
default:
assert false : "Unexpected state for progress bar.";
shownState = CircularProgressView.UiState.RETRY;
break;
}
switch (item.state) {
case OfflineItemState.INTERRUPTED: // Intentional fallthrough.
case OfflineItemState.PAUSED: // Intentional fallthrough.
case OfflineItemState.PENDING:
shownProgress = inactiveProgress;
break;
case OfflineItemState.IN_PROGRESS:
shownProgress = activeProgress;
break;
case OfflineItemState.FAILED: // Intentional fallthrough.
case OfflineItemState.CANCELLED: // Intentional fallthrough.
shownProgress = 0;
break;
case OfflineItemState.COMPLETE: // Intentional fallthrough.
default:
assert false : "Unexpected state for progress bar.";
shownProgress = 0;
break;
}
view.setState(shownState);
view.setProgress(shownProgress);
}
} }
\ No newline at end of file
// 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.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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.CircularProgressView;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.widget.TintedImageButton;
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 InProgressVideoViewHolder extends ListItemViewHolder {
private final TextView mTitle;
private final TextView mCaption;
private final CircularProgressView mActionButton;
private final TintedImageButton mCancelButton;
/**
* Creates a new {@link InProgressViewHolder} instance.
*/
public static InProgressVideoViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.download_manager_in_progress_video_item, null);
return new InProgressVideoViewHolder(view);
}
/** Constructor. */
public InProgressVideoViewHolder(View view) {
super(view);
mTitle = view.findViewById(R.id.title);
mCaption = view.findViewById(R.id.caption);
mActionButton = view.findViewById(R.id.action_button);
mCancelButton = view.findViewById(R.id.cancel_button);
}
// ListItemViewHolder implementation.
@Override
public void bind(PropertyModel properties, ListItem item) {
OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
mTitle.setText(offlineItem.title);
mCancelButton.setOnClickListener(
v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem));
// TODO(shaktisahu): Create status string for the new specs.
mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.progress));
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;
}
});
}
}
...@@ -7,15 +7,17 @@ package org.chromium.chrome.browser.download.home.list.holder; ...@@ -7,15 +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.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.chrome.browser.download.DownloadUtils; import org.chromium.chrome.browser.download.DownloadUtils;
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.ListProperties;
import org.chromium.chrome.browser.download.home.list.UiUtils;
import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
import org.chromium.chrome.browser.modelutil.PropertyModel; import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.widget.TintedImageButton; import org.chromium.chrome.browser.widget.TintedImageButton;
import org.chromium.chrome.download.R; import org.chromium.chrome.download.R;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItemState; import org.chromium.components.offline_items_collection.OfflineItemState;
/** /**
...@@ -23,10 +25,9 @@ import org.chromium.components.offline_items_collection.OfflineItemState; ...@@ -23,10 +25,9 @@ import org.chromium.components.offline_items_collection.OfflineItemState;
* OfflineItem}. * OfflineItem}.
*/ */
public class InProgressViewHolder extends ListItemViewHolder { public class InProgressViewHolder extends ListItemViewHolder {
private final ProgressBar mProgressBar;
private final TextView mTitle; private final TextView mTitle;
private final TextView mCaption; private final TextView mCaption;
private final TintedImageButton mPauseResumeButton; private final CircularProgressView mActionButton;
private final TintedImageButton mCancelButton; private final TintedImageButton mCancelButton;
/** /**
...@@ -41,54 +42,42 @@ public class InProgressViewHolder extends ListItemViewHolder { ...@@ -41,54 +42,42 @@ public class InProgressViewHolder extends ListItemViewHolder {
/** Constructor. */ /** Constructor. */
public InProgressViewHolder(View view) { public InProgressViewHolder(View view) {
super(view); super(view);
mProgressBar = view.findViewById(R.id.progress_bar);
mTitle = view.findViewById(R.id.title); mTitle = view.findViewById(R.id.title);
mCaption = view.findViewById(R.id.caption); mCaption = view.findViewById(R.id.caption);
mPauseResumeButton = view.findViewById(R.id.pause_button); mActionButton = view.findViewById(R.id.action_button);
mCancelButton = view.findViewById(R.id.cancel_button); mCancelButton = view.findViewById(R.id.cancel_button);
} }
// ListItemViewHolder implementation. // ListItemViewHolder implementation.
@Override @Override
public void bind(PropertyModel properties, ListItem item) { public void bind(PropertyModel properties, ListItem item) {
ListItem.OfflineItemListItem offlineItem = (ListItem.OfflineItemListItem) item; OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
mTitle.setText(offlineItem.item.title);
mCancelButton.setOnClickListener(
v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem.item));
if (offlineItem.item.state == OfflineItemState.PAUSED) {
mPauseResumeButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
mPauseResumeButton.setContentDescription(
itemView.getContext().getString(R.string.download_notification_resume_button));
} else {
mPauseResumeButton.setImageResource(R.drawable.ic_pause_white_24dp);
mPauseResumeButton.setContentDescription(
itemView.getContext().getString(R.string.download_notification_pause_button));
}
mTitle.setText(offlineItem.title);
mCancelButton.setOnClickListener(
v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem));
// TODO(shaktisahu): Create status string for the new specs. // TODO(shaktisahu): Create status string for the new specs.
mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.item.progress)); mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.progress));
mPauseResumeButton.setOnClickListener(view -> {
if (offlineItem.item.state == OfflineItemState.PAUSED) { UiUtils.setProgressForOfflineItem(mActionButton, offlineItem);
properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem.item); mActionButton.setOnClickListener(view -> {
} else { switch (offlineItem.state) {
properties.get(ListProperties.CALLBACK_PAUSE).onResult(offlineItem.item); 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;
} }
}); });
boolean showIndeterminate = offlineItem.item.progress.isIndeterminate()
&& offlineItem.item.state != OfflineItemState.PAUSED
&& offlineItem.item.state != OfflineItemState.PENDING;
if (showIndeterminate) {
mProgressBar.setIndeterminate(true);
mProgressBar.setIndeterminateDrawable(itemView.getContext().getResources().getDrawable(
R.drawable.download_circular_progress_bar));
} else {
mProgressBar.setIndeterminate(false);
}
if (!offlineItem.item.progress.isIndeterminate()) {
mProgressBar.setProgress(offlineItem.item.progress.getPercentage());
}
} }
} }
...@@ -49,6 +49,8 @@ public abstract class ListItemViewHolder extends ViewHolder { ...@@ -49,6 +49,8 @@ public abstract class ListItemViewHolder extends ViewHolder {
return SeparatorViewHolder.create(parent, true); return SeparatorViewHolder.create(parent, true);
case ListUtils.ViewType.SEPARATOR_SECTION: case ListUtils.ViewType.SEPARATOR_SECTION:
return SeparatorViewHolder.create(parent, false); return SeparatorViewHolder.create(parent, false);
case ListUtils.ViewType.IN_PROGRESS_VIDEO:
return InProgressVideoViewHolder.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.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import android.support.annotation.StringRes;
import android.support.annotation.StyleableRes;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import org.chromium.chrome.browser.util.MathUtils;
import org.chromium.chrome.download.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A representation of a progress bar that supports (1) an indeterminate state, (2) a determinate
* state, and (3) running, paused, and retry states.
*
* The determinate {@link Drawable} will have it's level set via {@link Drawable#setLevel(int)}
* based on the progress (0 - 10,000).
*
* The indeterminate {@link Drawable} supports {@link Animatable} drawables and the animation will
* be started/stopped when shown/hidden respectively.
*/
public class CircularProgressView extends AppCompatImageButton {
/**
* The value to use with {@link #setProgress(int)} to specify that the indeterminate
* {@link Drawable} should be used.
*/
public static final int INDETERMINATE = -1;
/** The various states this {@link CircularProgressView} can be in. */
@IntDef({UiState.RUNNING, UiState.PAUSED, UiState.RETRY})
@Retention(RetentionPolicy.SOURCE)
public @interface UiState {
/** This progress bar will look like it is actively running based on the XML drawable. */
int RUNNING = 0;
/** This progress bar will look like it is paused based on the XML drawable. */
int PAUSED = 1;
/** This progress bar will look like it is able to be retried based on the XML drawable. */
int RETRY = 2;
}
private static final int MAX_LEVEL = 10000;
private final Drawable mIndeterminateProgress;
private final Drawable mDeterminateProgress;
private final Drawable mResumeButtonSrc;
private final Drawable mPauseButtonSrc;
private final Drawable mRetryButtonSrc;
// Tracking this here as the API {@link View#getForeground()} is not available in all supported
// Android versions.
private Drawable mForegroundDrawable;
/**
* Creates an instance of a {@link CircularProgressView}.
* @param context The {@link Context} to use.
* @param attrs An {@link AttributeSet} instance.
*/
public CircularProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray types =
context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView, 0, 0);
try {
mIndeterminateProgress = getDrawable(
context, types, R.styleable.CircularProgressView_indeterminateProgress);
mDeterminateProgress = getDrawable(
context, types, R.styleable.CircularProgressView_determinateProgress);
mResumeButtonSrc =
getDrawable(context, types, R.styleable.CircularProgressView_resumeSrc);
mPauseButtonSrc =
getDrawable(context, types, R.styleable.CircularProgressView_pauseSrc);
mRetryButtonSrc =
getDrawable(context, types, R.styleable.CircularProgressView_retrySrc);
} finally {
types.recycle();
}
}
/**
* Sets the progress of this {@link CircularProgressView} to {@code progress}. If {@code
* progress} is {@link #INDETERMINATE} an indeterminate {@link Drawable} will be used.
* Otherwise the value will be clamped between 0 and 100 and a determinate {@link Drawable} will
* be used and have it's level set via {@link Drawable#setLevel(int)}.
*
* @param progress The progress value (0 to 100 or {@link #INDETERMINATE}) to show.
*/
public void setProgress(int progress) {
if (progress == INDETERMINATE) {
if (mForegroundDrawable != mIndeterminateProgress) {
setForeground(mIndeterminateProgress);
if (mIndeterminateProgress instanceof Animatable) {
((Animatable) mIndeterminateProgress).start();
}
}
} else {
progress = MathUtils.clamp(progress, 0, 100);
mDeterminateProgress.setLevel(progress * MAX_LEVEL / 100);
setForeground(mDeterminateProgress);
}
// Stop any animation that might have previously been running.
if (mForegroundDrawable != mIndeterminateProgress) {
((Animatable) mIndeterminateProgress).stop();
}
}
/**
* The state this {@link CircularProgressView} should show. This can be one of the three
* UiStates defined above. This will determine what the action drawable is in the view.
* @param state The UiState to use.
*/
public void setState(@UiState int state) {
Drawable imageDrawable;
@StringRes
int contentDescription;
switch (state) {
case UiState.RUNNING:
imageDrawable = mPauseButtonSrc;
contentDescription = R.string.download_notification_pause_button;
break;
case UiState.PAUSED:
imageDrawable = mResumeButtonSrc;
contentDescription = R.string.download_notification_resume_button;
break;
case UiState.RETRY:
default:
imageDrawable = mRetryButtonSrc;
contentDescription = R.string.download_notification_resume_button;
break;
}
setImageDrawable(imageDrawable);
setContentDescription(getContext().getText(contentDescription));
}
// View implementation.
@Override
public void setForeground(Drawable foreground) {
mForegroundDrawable = foreground;
super.setForeground(foreground);
}
private static final Drawable getDrawable(
Context context, TypedArray attrs, @StyleableRes int attrId) {
@DrawableRes
int resId = attrs.getResourceId(attrId, -1);
if (resId == -1) return null;
return AppCompatResources.getDrawable(context, resId);
}
}
\ No newline at end of file
...@@ -10,7 +10,7 @@ import android.support.graphics.drawable.Animatable2Compat; ...@@ -10,7 +10,7 @@ import android.support.graphics.drawable.Animatable2Compat;
import android.support.graphics.drawable.AnimatedVectorDrawableCompat; import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
import android.widget.ImageView; import android.widget.ImageView;
import org.chromium.chrome.R; import org.chromium.chrome.download.R;
/** /**
* A helper class to display the loading image and animation for image and video items. This class * A helper class to display the loading image and animation for image and video items. This class
......
...@@ -509,6 +509,7 @@ chrome_java_sources = [ ...@@ -509,6 +509,7 @@ 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/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/MoreButtonViewHolder.java",
...@@ -517,6 +518,7 @@ chrome_java_sources = [ ...@@ -517,6 +518,7 @@ chrome_java_sources = [
"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/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/CircularProgressView.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",
"java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java", "java/src/org/chromium/chrome/browser/download/home/list/ListUtils.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