Commit 3a35b652 authored by Shakti Sahu's avatar Shakti Sahu Committed by Commit Bot

Download Home : Added section titles

1- Modified the mutator logic to maintain the items in a date/section
   ordered hierarchical form
2- The mutator will add views for sections, section separators and
   date separators.

Several other smaller details such as :
1 - First section should have less spacing between the preceding date.
2 - The last date/section shouldn't have a separator.


Bug: 866307
Change-Id: I6eca116c26f403760160d016655fad3c05278c3a
Reviewed-on: https://chromium-review.googlesource.com/1151062
Commit-Queue: Shakti Sahu <shaktisahu@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581045}
parent bc9d7d70
......@@ -7,11 +7,10 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingStart="12dp"
android:paddingTop="@dimen/download_manager_section_title_padding_top"
android:paddingBottom="@dimen/download_manager_section_title_padding_bottom"
android:paddingStart="@dimen/list_item_default_margin"
android:maxLines="1"
android:gravity="start|center_vertical"
android:textAppearance="@style/BlackTitle2"
android:textAlignment="viewStart"
/>
\ No newline at end of file
android:textAppearance="@style/BlackTitle1"
android:textAlignment="viewStart" />
\ 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.
-->
<View
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Divider"
android:minHeight="2dp" />
\ 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.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/download_manager_section_title_padding_top"
android:paddingBottom="@dimen/download_manager_section_title_padding_bottom"
android:paddingStart="@dimen/list_item_default_margin"
android:gravity="start|center_vertical"
android:textAppearance="@style/BlackHint2"
android:textAlignment="viewStart" />
\ 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
style="@style/Divider"
android:layout_marginStart="16dp" />
</FrameLayout>
\ No newline at end of file
......@@ -584,6 +584,10 @@
<dimen name="download_manager_prefetch_thumbnail_size">114dp</dimen>
<dimen name="download_manager_prefetch_horizontal_margin">16dp</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_condensed">0dp</dimen>
<dimen name="download_manager_section_title_padding_bottom">0dp</dimen>
<dimen name="download_manager_section_title_padding_image">8dp</dimen>
<!-- Navigation history popup dimensions -->
<dimen name="navigation_popup_width">312dp</dimen>
......
......@@ -39,7 +39,7 @@ public class Filters {
* @param filter The {@link OfflineItem#filter} type to convert.
* @return The corresponding {@link FilterType}.
*/
public static @FilterType int fromOfflineItem(@OfflineItemFilter int filter) {
public static @FilterType Integer fromOfflineItem(@OfflineItemFilter int filter) {
switch (filter) {
case OfflineItemFilter.FILTER_PAGE:
return FilterType.SITES;
......
......@@ -8,13 +8,14 @@ import android.support.annotation.Nullable;
import android.support.v7.util.BatchingListUpdateCallback;
import android.support.v7.util.ListUpdateCallback;
import org.chromium.chrome.browser.modelutil.ListObservableImpl;
import org.chromium.chrome.browser.modelutil.SimpleListObservable;
/**
* Helper class to batch updates to ListObservable before notifying observers.
* Helper class to batch updates to SimpleListObservable before notifying observers.
* @see BatchingListUpdateCallback
* @param <T> The object type that this class manages in a list.
*/
public abstract class BatchListObservable extends ListObservableImpl<Void> {
public abstract class BatchListObservable<T> extends SimpleListObservable<T> {
final BatchingListUpdateCallback mBatchingCallback;
/** Creates a new BatchListObservable instance. */
......
......@@ -72,7 +72,7 @@ class DateOrderedListMediator {
boolean selected = mSelectionDelegate.isItemSelected(item);
item.showSelectedAnimation = selected && !item.selected;
item.selected = selected;
mModel.setItem(i, item);
mModel.update(i, item);
}
mModel.dispatchLastEvent();
mModel.getProperties().setValue(
......
......@@ -14,6 +14,10 @@ import java.util.Date;
/** An abstract class that represents a variety of possible list items to show in downloads home. */
public abstract class ListItem {
private static final long DATE_SEPARATOR_HASH_CODE_OFFSET = 10;
private static final long SECTION_SEPARATOR_HASH_CODE_OFFSET = 100;
private static final long SECTION_HEADER_HASH_CODE_OFFSET = 1000;
public final long stableId;
/** Indicates that we are in multi-select mode and the item is currently selected. */
......@@ -66,6 +70,65 @@ public abstract class ListItem {
}
}
/** A {@link ListItem} representing a section header. */
public static class SectionHeaderListItem extends DateListItem {
public final int filter;
public boolean isFirstSectionOfDay;
/**
* Creates a {@link SectionHeaderListItem} instance for a given {@code filter} and
* {@code timestamp}.
*/
public SectionHeaderListItem(int filter, long timestamp) {
super(generateStableId(timestamp, filter), new Date(timestamp));
this.filter = filter;
}
@VisibleForTesting
static long generateStableId(long timestamp, int filter) {
long hash = new Date(timestamp).hashCode();
return hash + filter + SECTION_HEADER_HASH_CODE_OFFSET;
}
}
/** A {@link ListItem} representing a divider that separates sections and dates. */
public static class SeparatorViewListItem extends DateListItem {
private final boolean mIsDateDivider;
/**
* Creates a separator to be shown at the end of a given date.
* @param timestamp The date corresponding to this group of downloads.
*/
public SeparatorViewListItem(long timestamp) {
super(generateStableId(timestamp), new Date(timestamp));
mIsDateDivider = true;
}
/**
* Creates a separator to be shown at the end of a section for a given section on a given
* date.
* @param timestamp The date corresponding to the section.
* @param filter The type of downloads contained in this section.
*/
public SeparatorViewListItem(long timestamp, int filter) {
super(generateStableId(timestamp, filter), new Date(timestamp));
mIsDateDivider = false;
}
/** Whether this view represents a date divider. */
public boolean isDateDivider() {
return mIsDateDivider;
}
private static long generateStableId(long timestamp) {
return ((long) (new Date(timestamp).hashCode())) + DATE_SEPARATOR_HASH_CODE_OFFSET;
}
private static long generateStableId(long timestamp, int filter) {
return generateStableId(timestamp) + filter + SECTION_SEPARATOR_HASH_CODE_OFFSET;
}
}
/** A {@link ListItem} that involves a {@link OfflineItem}. */
public static class OfflineItemListItem extends DateListItem {
public final OfflineItem item;
......
......@@ -4,20 +4,14 @@
package org.chromium.chrome.browser.download.home.list;
import org.chromium.chrome.browser.modelutil.ListObservable;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.SimpleList;
import java.util.ArrayList;
import java.util.List;
/**
* This model represents the data required to build a list UI around a set of {@link ListItem}s.
* This includes (1) a {@link ListObservable} implementation and (2) exposing a
* This includes (1) a {@link BatchListObservable} implementation and (2) exposing a
* {@link PropertyModel} for shared item properties and general list information.
*/
class ListItemModel extends BatchListObservable implements SimpleList<ListItem> {
private final List<ListItem> mItems = new ArrayList<>();
class ListItemModel extends BatchListObservable<ListItem> {
private final PropertyModel mListProperties = new PropertyModel(ListProperties.ALL_KEYS);
/**
......@@ -27,33 +21,4 @@ class ListItemModel extends BatchListObservable implements SimpleList<ListItem>
public PropertyModel getProperties() {
return mListProperties;
}
/** Adds {@code item} to this list at {@code index}. */
public void addItem(int index, ListItem item) {
mItems.add(index, item);
notifyItemInserted(index);
}
/** Removes the {@link ListItem} at {@code index}. */
public void removeItem(int index) {
mItems.remove(index);
notifyItemRemoved(index);
}
/** Sets the {@link ListItem} at {@code index} to {@code item}. */
public void setItem(int index, ListItem item) {
mItems.set(index, item);
notifyItemChanged(index);
}
// SimpleList implementation.
@Override
public ListItem get(int index) {
return mItems.get(index);
}
@Override
public int size() {
return mItems.size();
}
}
\ No newline at end of file
}
......@@ -5,7 +5,9 @@
package org.chromium.chrome.browser.download.home.list;
import android.support.annotation.IntDef;
import android.support.annotation.StringRes;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.download.home.list.ListItem.DateListItem;
import org.chromium.chrome.browser.download.home.list.ListItem.OfflineItemListItem;
import org.chromium.chrome.browser.download.home.list.ListItem.ViewListItem;
......@@ -19,7 +21,8 @@ import java.lang.annotation.RetentionPolicy;
public class ListUtils {
/** The potential types of list items that could be displayed. */
@IntDef({ViewType.DATE, ViewType.IN_PROGRESS, ViewType.GENERIC, ViewType.VIDEO, ViewType.IMAGE,
ViewType.CUSTOM_VIEW, ViewType.PREFETCH})
ViewType.CUSTOM_VIEW, ViewType.PREFETCH, ViewType.SECTION_HEADER,
ViewType.SEPARATOR_DATE, ViewType.SEPARATOR_SECTION})
@Retention(RetentionPolicy.SOURCE)
public @interface ViewType {
int DATE = 0;
......@@ -29,6 +32,9 @@ public class ListUtils {
int IMAGE = 4;
int CUSTOM_VIEW = 5;
int PREFETCH = 6;
int SECTION_HEADER = 7;
int SEPARATOR_DATE = 8;
int SEPARATOR_SECTION = 9;
}
private ListUtils() {}
......@@ -42,6 +48,12 @@ public class ListUtils {
*/
public static @ViewType int getViewTypeForItem(ListItem item) {
if (item instanceof ViewListItem) return ViewType.CUSTOM_VIEW;
if (item instanceof ListItem.SectionHeaderListItem) return ViewType.SECTION_HEADER;
if (item instanceof ListItem.SeparatorViewListItem) {
ListItem.SeparatorViewListItem separator = (ListItem.SeparatorViewListItem) item;
return separator.isDateDivider() ? ViewType.SEPARATOR_DATE : ViewType.SEPARATOR_SECTION;
}
if (item instanceof DateListItem) {
if (item instanceof OfflineItemListItem) {
OfflineItemListItem offlineItem = (OfflineItemListItem) item;
......@@ -76,6 +88,28 @@ public class ListUtils {
return ViewType.GENERIC;
}
/**
* @return The id of the string to be displayed as the section header for the given filter.
*/
public static @StringRes int getTextForSection(int filter) {
switch (filter) {
case OfflineItemFilter.FILTER_PAGE:
return R.string.download_manager_ui_pages;
case OfflineItemFilter.FILTER_IMAGE:
return R.string.download_manager_ui_images;
case OfflineItemFilter.FILTER_VIDEO:
return R.string.download_manager_ui_video;
case OfflineItemFilter.FILTER_AUDIO:
return R.string.download_manager_ui_audio;
case OfflineItemFilter.FILTER_OTHER:
return R.string.download_manager_ui_other;
case OfflineItemFilter.FILTER_DOCUMENT:
return R.string.download_manager_ui_documents;
default:
return R.string.download_manager_ui_all_downloads;
}
}
/**
* Analyzes a {@link ListItem} and finds the best span size based on the current state. Span
* size determines how many columns this {@link ListItem}'s {@link View} will take up in the
......
......@@ -43,6 +43,12 @@ public abstract class ListItemViewHolder extends ViewHolder {
return new CustomViewHolder(parent);
case ListUtils.ViewType.PREFETCH:
return PrefetchViewHolder.create(parent);
case ListUtils.ViewType.SECTION_HEADER:
return SectionTitleViewHolder.create(parent);
case ListUtils.ViewType.SEPARATOR_DATE:
return SeparatorViewHolder.create(parent, true);
case ListUtils.ViewType.SEPARATOR_SECTION:
return SeparatorViewHolder.create(parent, 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.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.ListUtils;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.components.offline_items_collection.OfflineItemFilter;
/**
* A {@link ViewHolder} specifically meant to display a section header.
*/
public class SectionTitleViewHolder extends ListItemViewHolder {
/** Create a new {@link SectionTitleViewHolder} instance. */
public static SectionTitleViewHolder create(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.download_manager_section_header, null);
return new SectionTitleViewHolder(view);
}
private SectionTitleViewHolder(View view) {
super(view);
}
// ListItemViewHolder implementation.
@Override
public void bind(PropertyModel properties, ListItem item) {
ListItem.SectionHeaderListItem sectionItem = (ListItem.SectionHeaderListItem) item;
((TextView) itemView).setText(ListUtils.getTextForSection(sectionItem.filter));
boolean isPhoto = sectionItem.filter == OfflineItemFilter.FILTER_IMAGE;
Resources resources = itemView.getContext().getResources();
int paddingTop = resources.getDimensionPixelSize(isPhoto
? R.dimen.download_manager_section_title_padding_image
: R.dimen.download_manager_section_title_padding_top);
int paddingBottom = resources.getDimensionPixelSize(isPhoto
? R.dimen.download_manager_section_title_padding_image
: R.dimen.download_manager_section_title_padding_bottom);
if (sectionItem.isFirstSectionOfDay) {
paddingTop = resources.getDimensionPixelSize(
R.dimen.download_manager_section_title_padding_top_condensed);
}
itemView.setPadding(
itemView.getPaddingLeft(), paddingTop, itemView.getPaddingRight(), paddingBottom);
}
}
// 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 org.chromium.chrome.R;
import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.modelutil.PropertyModel;
/**
* A {@link ViewHolder} specifically meant to display a separator.
*/
public class SeparatorViewHolder extends ListItemViewHolder {
/**
* Creates a new {@link SeparatorViewHolder} instance.
* @param isDateDivider Whether the divider is between dates or individual sections.
*/
public static SeparatorViewHolder create(ViewGroup parent, boolean isDateDivider) {
View dividerView =
LayoutInflater.from(parent.getContext())
.inflate(isDateDivider ? R.layout.download_manager_date_separator
: R.layout.download_manager_section_separator,
null);
return new SeparatorViewHolder(dividerView);
}
private SeparatorViewHolder(View view) {
super(view);
}
// ListItemViewHolder implementation.
@Override
public void bind(PropertyModel properties, ListItem item) {}
}
......@@ -495,6 +495,8 @@ chrome_java_sources = [
"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/PrefetchViewHolder.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/ThumbnailAwareViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java",
"java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java",
......
......@@ -18,17 +18,17 @@ import org.chromium.components.offline_items_collection.OfflineItemFilter;
public class FiltersTest {
@Test
public void testFilterConversions() {
Assert.assertEquals(
Filters.FilterType.SITES, Filters.fromOfflineItem(OfflineItemFilter.FILTER_PAGE));
Assert.assertEquals(
Filters.FilterType.VIDEOS, Filters.fromOfflineItem(OfflineItemFilter.FILTER_VIDEO));
Assert.assertEquals(
Filters.FilterType.MUSIC, Filters.fromOfflineItem(OfflineItemFilter.FILTER_AUDIO));
Assert.assertEquals(
Filters.FilterType.IMAGES, Filters.fromOfflineItem(OfflineItemFilter.FILTER_IMAGE));
Assert.assertEquals(
Filters.FilterType.OTHER, Filters.fromOfflineItem(OfflineItemFilter.FILTER_OTHER));
Assert.assertEquals(Filters.FilterType.OTHER,
Assert.assertEquals(Integer.valueOf(Filters.FilterType.SITES),
Filters.fromOfflineItem(OfflineItemFilter.FILTER_PAGE));
Assert.assertEquals(Integer.valueOf(Filters.FilterType.VIDEOS),
Filters.fromOfflineItem(OfflineItemFilter.FILTER_VIDEO));
Assert.assertEquals(Integer.valueOf(Filters.FilterType.MUSIC),
Filters.fromOfflineItem(OfflineItemFilter.FILTER_AUDIO));
Assert.assertEquals(Integer.valueOf(Filters.FilterType.IMAGES),
Filters.fromOfflineItem(OfflineItemFilter.FILTER_IMAGE));
Assert.assertEquals(Integer.valueOf(Filters.FilterType.OTHER),
Filters.fromOfflineItem(OfflineItemFilter.FILTER_OTHER));
Assert.assertEquals(Integer.valueOf(Filters.FilterType.OTHER),
Filters.fromOfflineItem(OfflineItemFilter.FILTER_DOCUMENT));
}
}
\ No newline at end of file
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