Commit ab029197 authored by Cathy Li's avatar Cathy Li Committed by Commit Bot

[Explore sites]: Create category card recycler view.

Bug: 867488
Change-Id: Ied05d87e4d1a104f4a6012d465fcf20fd85c3bcf
Reviewed-on: https://chromium-review.googlesource.com/1225935
Commit-Queue: Cathy Li <chili@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#593689}
parent f3caad1d
<?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.explore_sites.ExploreSitesCategoryCardView
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" >
<TextView
android:id="@+id/category_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:lines="1"
android:textAppearance="@style/BlackTitle1" />
<GridLayout
android:id="@+id/category_sites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="4"
android:rowCount="2" />
</org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView>
<?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.explore_sites.ExploreSitesPageLayout
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="match_parent" >
<android.support.v7.widget.RecyclerView
android:id="@+id/explore_sites_category_recycler"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</org.chromium.chrome.browser.explore_sites.ExploreSitesPageLayout>
...@@ -3,8 +3,13 @@ ...@@ -3,8 +3,13 @@
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. -->
<android.support.v7.widget.RecyclerView <org.chromium.chrome.browser.explore_sites.ExploreSitesTileView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent" android:layout_width="@dimen/tile_view_width"
android:layout_width="match_parent" /> android:layout_height="wrap_content">
<include
layout="@layout/tile_view_modern"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</org.chromium.chrome.browser.explore_sites.ExploreSitesTileView>
// 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.explore_sites;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.modelutil.ListObservableImpl;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyObservable;
import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Recycler view adapter delegate for a model containing a list of category cards and an error code.
*/
public class ExploreSitesCategoryCardAdapter extends ListObservableImpl<ExploreSitesCategory>
implements RecyclerViewAdapter.Delegate<
ExploreSitesCategoryCardViewHolderFactory.CategoryCardViewHolder,
ExploreSitesCategory>,
PropertyObservable.PropertyObserver<PropertyKey> {
@IntDef({ViewType.HEADER, ViewType.CATEGORY, ViewType.LOADING, ViewType.ERROR})
@Retention(RetentionPolicy.SOURCE)
public @interface ViewType {
int HEADER = 0;
int CATEGORY = 1;
int LOADING = 2;
int ERROR = 3;
}
private RecyclerView.LayoutManager mLayoutManager;
private PropertyModel mCategoryModel;
private RoundedIconGenerator mIconGenerator;
public ExploreSitesCategoryCardAdapter(PropertyModel model,
RecyclerView.LayoutManager layoutManager, RoundedIconGenerator iconGenerator) {
model.addObserver(this);
mCategoryModel = model;
mIconGenerator = iconGenerator;
}
@Override
public int getItemCount() {
// If not loaded, return 2 for title and error/loading section
if (mCategoryModel.get(ExploreSitesPage.STATUS_KEY)
!= ExploreSitesPage.CatalogLoadingState.SUCCESS) {
return 2;
}
// Add 1 for title.
return mCategoryModel.get(ExploreSitesPage.CATEGORY_LIST_KEY).size() + 1;
}
@Override
@ViewType
public int getItemViewType(int position) {
if (position == 0) return ViewType.HEADER;
if (mCategoryModel.get(ExploreSitesPage.STATUS_KEY)
== ExploreSitesPage.CatalogLoadingState.ERROR) {
return ViewType.ERROR;
}
if (mCategoryModel.get(ExploreSitesPage.STATUS_KEY)
== ExploreSitesPage.CatalogLoadingState.LOADING) {
return ViewType.LOADING;
}
return ViewType.CATEGORY;
}
@Override
public void onBindViewHolder(
ExploreSitesCategoryCardViewHolderFactory.CategoryCardViewHolder holder, int position,
@Nullable ExploreSitesCategory payload) {
if (holder.getItemViewType() == ViewType.HEADER) {
TextView view = (TextView) holder.itemView;
view.setText(R.string.explore_sites_title);
} else if (holder.getItemViewType() == ViewType.ERROR) {
// populate the error view
} else if (holder.getItemViewType() == ViewType.LOADING) {
// Populate loading view
} else {
ExploreSitesCategoryCardView view = (ExploreSitesCategoryCardView) holder.itemView;
view.setIconGenerator(mIconGenerator);
// Position - 1 because there is always title.
view.initialize(
mCategoryModel.get(ExploreSitesPage.CATEGORY_LIST_KEY).get(position - 1));
}
}
@Override
public void onPropertyChanged(
PropertyObservable<PropertyKey> source, @Nullable PropertyKey key) {
if (key == ExploreSitesPage.STATUS_KEY) {
int status = mCategoryModel.get(ExploreSitesPage.STATUS_KEY);
if (status == ExploreSitesPage.CatalogLoadingState.LOADING
|| status == ExploreSitesPage.CatalogLoadingState.ERROR) {
notifyItemChanged(1);
} // Else the list observer takes care of updating.
}
if (key == ExploreSitesPage.SCROLL_TO_CATEGORY_KEY) {
int pos = mCategoryModel.get(ExploreSitesPage.SCROLL_TO_CATEGORY_KEY);
mLayoutManager.scrollToPosition(pos + 1);
}
}
}
// 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.explore_sites;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.GridLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import java.util.List;
/**
* View for a category name and site tiles.
*/
public class ExploreSitesCategoryCardView extends LinearLayout {
private TextView mTitleView;
private GridLayout mTileView;
private RoundedIconGenerator mIconGenerator;
public ExploreSitesCategoryCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitleView = findViewById(R.id.category_title);
mTileView = findViewById(R.id.category_sites);
}
public void setIconGenerator(RoundedIconGenerator iconGenerator) {
mIconGenerator = iconGenerator;
}
public void initialize(ExploreSitesCategory category) {
updateTitle(category.getTitle());
updateTileViews(category.getSites());
}
public void updateTitle(String categoryTitle) {
mTitleView.setText(categoryTitle);
}
public void updateTileViews(List<ExploreSitesSite> sites) {
// Remove extra views if too many.
if (mTileView.getChildCount() > sites.size()) {
mTileView.removeViews(sites.size(), mTileView.getChildCount() - sites.size());
}
// Add views if too few.
if (mTileView.getChildCount() < sites.size()) {
for (int i = mTileView.getChildCount(); i < sites.size(); i++) {
mTileView.addView(LayoutInflater.from(getContext())
.inflate(R.layout.explore_sites_tile_view, mTileView));
}
}
// Initialize all the tiles again to update.
for (int i = 0; i < sites.size(); i++) {
ExploreSitesTileView tileView = (ExploreSitesTileView) mTileView.getChildAt(i);
tileView.setIconGenerator(mIconGenerator);
tileView.initialize(sites.get(i));
}
}
}
// 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.explore_sites;
import android.support.v7.widget.RecyclerView;
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.modelutil.RecyclerViewAdapter;
/** Factory to create CategoryCardViewHolder objects. */
public class ExploreSitesCategoryCardViewHolderFactory
implements RecyclerViewAdapter.ViewHolderFactory<
ExploreSitesCategoryCardViewHolderFactory.CategoryCardViewHolder> {
/** View holder for the recycler view. */
public static class CategoryCardViewHolder extends RecyclerView.ViewHolder {
public CategoryCardViewHolder(View view) {
super(view);
}
}
@Override
public CategoryCardViewHolder createViewHolder(
ViewGroup parent, @ExploreSitesCategoryCardAdapter.ViewType int viewType) {
View view;
switch (viewType) {
case ExploreSitesCategoryCardAdapter.ViewType.HEADER:
view = new TextView(parent.getContext());
break;
case ExploreSitesCategoryCardAdapter.ViewType.CATEGORY:
view = (ExploreSitesCategoryCardView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.explore_sites_category_card_view, parent);
break;
case ExploreSitesCategoryCardAdapter.ViewType.LOADING: // inflate loading spinny
case ExploreSitesCategoryCardAdapter.ViewType.ERROR: // inflate error
view = null;
break;
default:
assert false;
view = null;
}
return new CategoryCardViewHolder(view);
}
}
\ No newline at end of file
...@@ -5,36 +5,59 @@ ...@@ -5,36 +5,59 @@
package org.chromium.chrome.browser.explore_sites; package org.chromium.chrome.browser.explore_sites;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.native_page.BasicNativePage; import org.chromium.chrome.browser.native_page.BasicNativePage;
import org.chromium.chrome.browser.native_page.NativePageHost; import org.chromium.chrome.browser.native_page.NativePageHost;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import java.util.Collections; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
/** /**
* Provides functionality when the user interacts with the explore sites page. * Provides functionality when the user interacts with the explore sites page.
*/ */
public class ExploreSitesPage extends BasicNativePage { public class ExploreSitesPage extends BasicNativePage {
static final PropertyModel.WritableIntPropertyKey STATUS_KEY =
new PropertyModel.WritableIntPropertyKey();
static final PropertyModel.WritableIntPropertyKey SCROLL_TO_CATEGORY_KEY =
new PropertyModel.WritableIntPropertyKey();
static final PropertyModel
.ReadableObjectPropertyKey<ListModel<ExploreSitesCategory>> CATEGORY_LIST_KEY =
new PropertyModel.ReadableObjectPropertyKey<>();
@IntDef({CatalogLoadingState.LOADING, CatalogLoadingState.SUCCESS, CatalogLoadingState.ERROR})
@Retention(RetentionPolicy.SOURCE)
public @interface CatalogLoadingState {
int LOADING = 1;
int SUCCESS = 2;
int ERROR = 3;
}
private ViewGroup mView; private ViewGroup mView;
private String mTitle; private String mTitle;
private Activity mActivity; private Activity mActivity;
private List<ExploreSitesCategory> mCategoryData;
private ExploreSitesPageLayout mLayout; private ExploreSitesPageLayout mLayout;
private PropertyModel mModel;
/** /**
* Create a new instance of the explore sites page. * Create a new instance of the explore sites page.
*/ */
public ExploreSitesPage(ChromeActivity activity, NativePageHost host) { public ExploreSitesPage(ChromeActivity activity, NativePageHost host) {
super(activity, host); super(activity, host);
mCategoryData = Collections.emptyList();
} }
@Override @Override
...@@ -42,16 +65,33 @@ public class ExploreSitesPage extends BasicNativePage { ...@@ -42,16 +65,33 @@ public class ExploreSitesPage extends BasicNativePage {
mActivity = activity; mActivity = activity;
mTitle = mActivity.getString(R.string.explore_sites_title); mTitle = mActivity.getString(R.string.explore_sites_title);
mView = (ViewGroup) mActivity.getLayoutInflater().inflate( mView = (ViewGroup) mActivity.getLayoutInflater().inflate(
R.layout.explore_sites_main, null); R.layout.explore_sites_page_layout, null);
mModel = new PropertyModel.Builder(STATUS_KEY, SCROLL_TO_CATEGORY_KEY, CATEGORY_LIST_KEY)
.with(CATEGORY_LIST_KEY, new ListModel<ExploreSitesCategory>())
.with(SCROLL_TO_CATEGORY_KEY, 0)
.build();
Context context = mView.getContext();
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
int iconSizePx = context.getResources().getDimensionPixelSize(R.dimen.tile_view_icon_size);
RoundedIconGenerator iconGenerator =
new RoundedIconGenerator(iconSizePx, iconSizePx, iconSizePx / 2,
ApiCompatibilityUtils.getColor(
context.getResources(), R.color.default_favicon_background_color),
context.getResources().getDimensionPixelSize(R.dimen.headline_size_medium));
ExploreSitesCategoryCardAdapter adapterDelegate =
new ExploreSitesCategoryCardAdapter(mModel, layoutManager, iconGenerator);
Profile profile = host.getActiveTab().getProfile(); Profile profile = host.getActiveTab().getProfile();
ExploreSitesBridge.getEspCatalog(profile, this::translateToModel); ExploreSitesBridge.getEspCatalog(profile, this::translateToModel);
mLayout = new ExploreSitesPageLayout(mView, profile); mModel.set(STATUS_KEY, CatalogLoadingState.LOADING);
// TODO(chili): Set layout to be an observer of list model // TODO(chili): Set layout to be an observer of list model
} }
private void translateToModel(@Nullable List<ExploreSitesCategory> categoryList) { private void translateToModel(@Nullable List<ExploreSitesCategory> categoryList) {
// TODO(chili): Call listmodel.addAll ListModel<ExploreSitesCategory> categoryListModel = mModel.get(CATEGORY_LIST_KEY);
categoryListModel.set(categoryList);
} }
@Override @Override
......
// 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.explore_sites;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import org.chromium.chrome.browser.widget.tile.TileWithTextView;
/**
* View for a category name and site tiles.
*/
public class ExploreSitesTileView extends TileWithTextView {
private static final int TITLE_LINES = 1;
private final int mIconSizePx;
private RoundedIconGenerator mIconGenerator;
public ExploreSitesTileView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
mIconSizePx = getResources().getDimensionPixelSize(R.dimen.tile_view_icon_size);
}
public void initialize(ExploreSitesSite site) {
super.initialize(site.getTitle(), site.getUrl(), /* showOfflineBadge = */ false,
getDrawableForBitmap(site.getIcon(), site.getTitle()), TITLE_LINES,
TileWithTextView.Style.MODERN);
}
public void updateIcon(Bitmap iconImage, String text) {
setIconDrawable(getDrawableForBitmap(iconImage, text));
}
public Drawable getDrawableForBitmap(Bitmap image, String text) {
if (image == null) {
return new BitmapDrawable(getResources(), mIconGenerator.generateIconForText(text));
}
return ViewUtils.createRoundedBitmapDrawable(image, mIconSizePx / 2);
}
/**
* Sets icon generator. This is to help prevent an instance of icon generator
* being created for each tile.
*/
void setIconGenerator(RoundedIconGenerator generator) {
mIconGenerator = generator;
}
}
...@@ -563,10 +563,14 @@ chrome_java_sources = [ ...@@ -563,10 +563,14 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardAdapter.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewHolderFactory.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPageLayout.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPageLayout.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java", "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java",
"java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesTileView.java",
"java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java", "java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java",
"java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java", "java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java",
"java/src/org/chromium/chrome/browser/externalauth/VerifiedHandler.java", "java/src/org/chromium/chrome/browser/externalauth/VerifiedHandler.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