Refactored ListView into RecycleView

Implemented custom RecyclerView to support using an empty view and a
    auto fitted grid layout.

    Reenabled shared element transitions for Oreo
This commit is contained in:
Martijn Brekhof 2018-07-24 22:04:33 +02:00
parent f985f4132d
commit 3f5ccefe2e
41 changed files with 1223 additions and 891 deletions

View File

@ -102,6 +102,7 @@ dependencies {
implementation "com.android.support:preference-v14:${supportLibVersion}"
implementation "com.android.support:support-v13:${supportLibVersion}"
implementation "com.android.support:design:${supportLibVersion}"
implementation "com.android.support:recyclerview-v7:${supportLibVersion}"
implementation 'com.fasterxml.jackson.core:jackson-databind:2.5.2'
implementation 'com.jakewharton:butterknife:8.8.1'

View File

@ -27,6 +27,7 @@ import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
import android.view.LayoutInflater;
@ -35,8 +36,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.Toast;
@ -48,6 +47,7 @@ import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.service.library.SyncItem;
import org.xbmc.kore.service.library.SyncUtils;
import org.xbmc.kore.ui.viewgroups.RecyclerViewEmptyViewSupport;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
@ -78,6 +78,7 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
abstract protected void onListItemClicked(View view);
abstract protected CursorLoader createCursorLoader();
abstract protected RecyclerViewCursorAdapter createCursorAdapter();
@TargetApi(16)
@Nullable
@ -136,16 +137,21 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
}
@Override
protected AdapterView.OnItemClickListener createOnItemClickListener() {
return new AdapterView.OnItemClickListener() {
protected RecyclerViewEmptyViewSupport.OnItemClickListener createOnItemClickListener() {
return new RecyclerViewEmptyViewSupport.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
public void onItemClick(View view, int position) {
saveSearchState();
onListItemClicked(view);
}
};
}
@Override
final protected RecyclerView.Adapter createAdapter() {
return createCursorAdapter();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.abstractcursorlistfragment, menu);
@ -305,7 +311,7 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
loaderLoading = false;
((CursorAdapter) getAdapter()).swapCursor(cursor);
((RecyclerViewCursorAdapter) getAdapter()).swapCursor(cursor);
if (TextUtils.isEmpty(searchFilter)) {
// To prevent the empty text from appearing on the first load, set it now
emptyView.setText(getString(R.string.swipe_down_to_refresh));
@ -316,7 +322,7 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
/** {@inheritDoc} */
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
((CursorAdapter) getAdapter()).swapCursor(null);
((RecyclerViewCursorAdapter) getAdapter()).swapCursor(null);
}
/**

View File

@ -23,43 +23,37 @@ import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.TextView;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.ui.viewgroups.RecyclerViewEmptyViewSupport;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.Utils;
import butterknife.ButterKnife;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public abstract class AbstractListFragment extends Fragment implements
SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = LogUtils.makeLogTag(AbstractListFragment.class);
private BaseAdapter adapter;
private RecyclerView.Adapter adapter;
private final String BUNDLE_SAVEDINSTANCE_LISTPOSITION = "lposition";
private boolean gridViewUsesMultipleColumns;
private Unbinder unbinder;
protected @BindView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.list) GridView gridView;
@BindView(R.id.list) RecyclerViewEmptyViewSupport recyclerView;
@BindView(android.R.id.empty) TextView emptyView;
abstract protected AdapterView.OnItemClickListener createOnItemClickListener();
abstract protected BaseAdapter createAdapter();
abstract protected RecyclerViewEmptyViewSupport.OnItemClickListener createOnItemClickListener();
abstract protected RecyclerViewEmptyViewSupport.Adapter createAdapter();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@ -76,40 +70,18 @@ public abstract class AbstractListFragment extends Fragment implements
swipeRefreshLayout.setOnRefreshListener(this);
gridView.setEmptyView(emptyView);
gridView.setOnItemClickListener(createOnItemClickListener());
gridView.setAdapter(adapter);
recyclerView.setEmptyView(emptyView);
recyclerView.setOnItemClickListener(createOnItemClickListener());
if (savedInstanceState != null) {
final int listPosition = savedInstanceState.getInt(BUNDLE_SAVEDINSTANCE_LISTPOSITION);
gridView.post(new Runnable() {
@Override
public void run() {
gridView.setSelection(listPosition);
}
});
if (PreferenceManager
.getDefaultSharedPreferences(getActivity())
.getBoolean(Settings.KEY_PREF_SINGLE_COLUMN,
Settings.DEFAULT_PREF_SINGLE_COLUMN)) {
recyclerView.setColumnCount(1);
}
//Listener added to be able to determine if multiple-columns is at all possible for the current grid
//We use this information to enable/disable the menu item to switch between multiple and single columns
gridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (gridView.getNumColumns() > 1) {
gridViewUsesMultipleColumns = true;
}
if (Utils.isJellybeanOrLater()) {
gridView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
gridView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
//Make sure menu is update d if it was already created
getActivity().invalidateOptionsMenu();
}
});
recyclerView.setAdapter(adapter);
setHasOptionsMenu(true);
return root;
@ -121,34 +93,23 @@ public abstract class AbstractListFragment extends Fragment implements
unbinder.unbind();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (gridView != null) {
outState.putInt(BUNDLE_SAVEDINSTANCE_LISTPOSITION, gridView.getFirstVisiblePosition());
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.abstractlistfragment, menu);
if(gridViewUsesMultipleColumns) {
if(recyclerView.isMultiColumnSupported()) {
if (PreferenceManager
.getDefaultSharedPreferences(getActivity())
.getBoolean(Settings.KEY_PREF_SINGLE_COLUMN,
Settings.DEFAULT_PREF_SINGLE_COLUMN)) {
gridView.setNumColumns(1);
recyclerView.setColumnCount(1);
adapter.notifyDataSetChanged();
MenuItem item = menu.findItem(R.id.action_multi_single_columns);
item.setTitle(R.string.multi_column);
}
} else {
//Default number of columns for GridView is set to AUTO_FIT.
//When this leads to a single column it is not possible
//to switch to multiple columns. We therefore disable
//the menu item.
//Disable menu item when mult-column is not supported
MenuItem item = menu.findItem(R.id.action_multi_single_columns);
item.setTitle(R.string.multi_column);
item.setEnabled(false);
@ -170,14 +131,14 @@ public abstract class AbstractListFragment extends Fragment implements
private void toggleAmountOfColumns(MenuItem item) {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(getActivity()).edit();
if (gridView.getNumColumns() == 1) {
if (recyclerView.getColumnCount() == 1) {
editor.putBoolean(Settings.KEY_PREF_SINGLE_COLUMN, false);
item.setTitle(R.string.single_column);
gridView.setNumColumns(GridView.AUTO_FIT);
recyclerView.setColumnCount(RecyclerViewEmptyViewSupport.AUTO_FIT);
} else {
editor.putBoolean(Settings.KEY_PREF_SINGLE_COLUMN, true);
item.setTitle(R.string.multi_column);
gridView.setNumColumns(1);
recyclerView.setColumnCount(1);
}
editor.apply();
adapter.notifyDataSetChanged(); //force gridView to redraw
@ -187,7 +148,7 @@ public abstract class AbstractListFragment extends Fragment implements
swipeRefreshLayout.setRefreshing(false);
}
public BaseAdapter getAdapter() {
public RecyclerView.Adapter getAdapter() {
return adapter;
}

View File

@ -48,8 +48,8 @@ import org.xbmc.kore.jsonrpc.method.Player;
import org.xbmc.kore.jsonrpc.type.ListType;
import org.xbmc.kore.jsonrpc.type.PlayerType;
import org.xbmc.kore.ui.generic.NavigationDrawerFragment;
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
import org.xbmc.kore.ui.generic.VolumeControllerDialogFragmentListener;
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
import org.xbmc.kore.ui.widgets.MediaProgressIndicator;
import org.xbmc.kore.ui.widgets.NowPlayingPanel;
import org.xbmc.kore.ui.widgets.VolumeLevelIndicator;
@ -58,8 +58,8 @@ import org.xbmc.kore.utils.SharedElementTransition;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
import butterknife.ButterKnife;
import butterknife.BindView;
import butterknife.ButterKnife;
public abstract class BaseMediaActivity extends BaseActivity
implements HostConnectionObserver.ApplicationEventsObserver,
@ -141,7 +141,7 @@ public abstract class BaseMediaActivity extends BaseActivity
if (fragment == null) {
fragment = createFragment();
if (Utils.isLollipopAndPreOreo()) {
if (Utils.isLollipopOrLater()) {
fragment.setExitTransition(null);
fragment.setReenterTransition(TransitionInflater
.from(this)
@ -154,7 +154,7 @@ public abstract class BaseMediaActivity extends BaseActivity
.commit();
}
if (Utils.isLollipopAndPreOreo()) {
if (Utils.isLollipopOrLater()) {
sharedElementTransition.setupExitTransition(this, fragment);
}
@ -265,7 +265,7 @@ public abstract class BaseMediaActivity extends BaseActivity
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
// Set up transitions
if (Utils.isLollipopAndPreOreo()) {
if (Utils.isLollipopOrLater()) {
dataHolder.setPosterTransitionName(sharedImageView.getTransitionName());
sharedElementTransition.setupEnterTransition(this, fragTrans, fragment, sharedImageView);
} else {

View File

@ -0,0 +1,92 @@
/*
* Copyright 2018 Martijn Brekhof. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbmc.kore.ui;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.View;
abstract public class RecyclerViewCursorAdapter extends RecyclerView.Adapter<RecyclerViewCursorAdapter.CursorViewHolder> {
private boolean dataValid;
private int rowIDColumn;
private Cursor cursor;
@Override
public void onBindViewHolder(CursorViewHolder holder, int position) {
if (!dataValid) {
throw new IllegalStateException("Cannot bind viewholder when cursor is in invalid state.");
}
if (!cursor.moveToPosition(position)) {
throw new IllegalStateException("Could not move cursor to position " + position + " when trying to bind viewholder");
}
holder.bindView(cursor);
}
@Override
public int getItemCount() {
if (dataValid) {
return cursor.getCount();
} else {
return 0;
}
}
@Override
public long getItemId(int position) {
if (!dataValid) {
throw new IllegalStateException("Cursor is in an invalid state.");
}
if (!cursor.moveToPosition(position)) {
throw new IllegalStateException("Could not move cursor to position " + position);
}
return cursor.getLong(rowIDColumn);
}
public void swapCursor(Cursor newCursor) {
if (newCursor == cursor) {
return;
}
if (newCursor != null) {
cursor = newCursor;
rowIDColumn = cursor.getColumnIndexOrThrow("_id");
dataValid = true;
notifyDataSetChanged();
} else {
notifyItemRangeRemoved(0, getItemCount());
cursor = null;
rowIDColumn = -1;
dataValid = false;
}
}
abstract public static class CursorViewHolder extends RecyclerView.ViewHolder {
public CursorViewHolder(View itemView) {
super(itemView);
itemView.setTag(this);
}
/**
* Called to update the content of {@link RecyclerView.ViewHolder#itemView} this holder holds.
*/
abstract public void bindView(Cursor cursor);
}
}

View File

@ -15,18 +15,15 @@
*/
package org.xbmc.kore.ui.sections.addon;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -38,10 +35,12 @@ import org.xbmc.kore.jsonrpc.method.Addons;
import org.xbmc.kore.jsonrpc.type.AddonType;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.AbstractListFragment;
import org.xbmc.kore.ui.viewgroups.RecyclerViewEmptyViewSupport;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@ -65,10 +64,10 @@ public class AddonListFragment extends AbstractListFragment {
private Handler callbackHandler = new Handler();
@Override
protected AdapterView.OnItemClickListener createOnItemClickListener() {
return new AdapterView.OnItemClickListener() {
protected RecyclerViewEmptyViewSupport.OnItemClickListener createOnItemClickListener() {
return new RecyclerViewEmptyViewSupport.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
public void onItemClick(View view, int position) {
// Get the movie id from the tag
ViewHolder tag = (ViewHolder) view.getTag();
// Notify the activity
@ -77,10 +76,9 @@ public class AddonListFragment extends AbstractListFragment {
};
}
@Override
protected BaseAdapter createAdapter() {
return new AddonsAdapter(getActivity(), R.layout.grid_item_addon);
protected RecyclerView.Adapter createAdapter() {
return new AddonsAdapter(getActivity());
}
@Override
@ -88,7 +86,7 @@ public class AddonListFragment extends AbstractListFragment {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(false);
if (getAdapter().getCount() == 0)
if (getAdapter().getItemCount() == 0)
callGetAddonsAndSetup();
}
@ -172,7 +170,7 @@ public class AddonListFragment extends AbstractListFragment {
adapter.notifyDataSetChanged();
hideRefreshAnimation();
if (adapter.getCount() == 0) {
if (adapter.getItemCount() == 0) {
getEmptyView().setText(R.string.no_addons_found_refresh);
}
}
@ -190,15 +188,16 @@ public class AddonListFragment extends AbstractListFragment {
callbackHandler);
}
private class AddonsAdapter extends ArrayAdapter<AddonType.Details> {
private static class AddonsAdapter extends RecyclerView.Adapter {
private HostManager hostManager;
private int artWidth, artHeight;
private String author;
private String version;
private Context context;
public AddonsAdapter(Context context, int resource) {
super(context, resource);
private ArrayList<AddonType.Details> items = new ArrayList<>();
public AddonsAdapter(Context context) {
this.context = context;
this.hostManager = HostManager.getInstance(context);
// Get the art dimensions
@ -207,63 +206,96 @@ public class AddonListFragment extends AbstractListFragment {
Resources resources = context.getResources();
artWidth = resources.getDimensionPixelOffset(R.dimen.detail_poster_width_square);
artHeight = resources.getDimensionPixelOffset(R.dimen.detail_poster_height_square);
author = context.getString(R.string.author);
version = context.getString(R.string.version);
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getActivity())
.inflate(R.layout.grid_item_addon, parent, false);
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_addon, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)convertView.findViewById(R.id.title);
viewHolder.detailsView = (TextView)convertView.findViewById(R.id.details);
viewHolder.artView = (ImageView)convertView.findViewById(R.id.art);
convertView.setTag(viewHolder);
}
return new ViewHolder(view, context, hostManager, artWidth, artHeight);
}
final ViewHolder viewHolder = (ViewHolder)convertView.getTag();
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
AddonType.Details addonDetails = this.getItem(position);
((ViewHolder) holder).onBind(addonDetails);
}
viewHolder.dataHolder.setTitle(addonDetails.name);
viewHolder.dataHolder.setDescription(addonDetails.description);
viewHolder.dataHolder.setUndertitle(addonDetails.summary);
viewHolder.dataHolder.setFanArtUrl(addonDetails.fanart);
viewHolder.dataHolder.setPosterUrl(addonDetails.thumbnail);
viewHolder.dataHolder.setDetails(author + " " + addonDetails.author + "\n" +
version + " " +addonDetails.version);
viewHolder.dataHolder.getBundle().putString(AddonInfoFragment.BUNDLE_KEY_ADDONID, addonDetails.addonid);
viewHolder.dataHolder.getBundle().putBoolean(AddonInfoFragment.BUNDLE_KEY_BROWSABLE,
AddonType.Types.XBMC_PYTHON_PLUGINSOURCE.equals(addonDetails.type));
@Override
public int getItemCount() {
return items.size();
}
viewHolder.titleView.setText(viewHolder.dataHolder.getTitle());
viewHolder.detailsView.setText(addonDetails.summary);
public void clear() {
items.clear();
}
UIUtils.loadImageWithCharacterAvatar(getContext(), hostManager,
addonDetails.thumbnail, viewHolder.dataHolder.getTitle(),
viewHolder.artView, artWidth, artHeight);
public void add(AddonType.Details item) {
items.add(item);
}
if(Utils.isLollipopOrLater()) {
viewHolder.artView.setTransitionName("a"+addonDetails.addonid);
}
return convertView;
public AddonType.Details getItem(int position) {
return items.get(position);
}
}
/**
* View holder pattern
*/
public static class ViewHolder {
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView titleView;
TextView detailsView;
ImageView artView;
private static String author;
private static String version;
private HostManager hostManager;
int artWidth;
int artHeight;
Context context;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
if( author == null ) {
author = context.getString(R.string.author);
version = context.getString(R.string.version);
}
titleView = itemView.findViewById(R.id.title);
detailsView = itemView.findViewById(R.id.details);
artView = itemView.findViewById(R.id.art);
itemView.setTag(this);
}
public void onBind(AddonType.Details addonDetails) {
dataHolder.setTitle(addonDetails.name);
dataHolder.setDescription(addonDetails.description);
dataHolder.setUndertitle(addonDetails.summary);
dataHolder.setFanArtUrl(addonDetails.fanart);
dataHolder.setPosterUrl(addonDetails.thumbnail);
dataHolder.setDetails(author + " " + addonDetails.author + "\n" +
version + " " +addonDetails.version);
dataHolder.getBundle().putString(AddonInfoFragment.BUNDLE_KEY_ADDONID, addonDetails.addonid);
dataHolder.getBundle().putBoolean(AddonInfoFragment.BUNDLE_KEY_BROWSABLE,
AddonType.Types.XBMC_PYTHON_PLUGINSOURCE.equals(addonDetails.type));
titleView.setText(dataHolder.getTitle());
detailsView.setText(addonDetails.summary);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
addonDetails.thumbnail, dataHolder.getTitle(),
artView, artWidth, artHeight);
if(Utils.isLollipopOrLater()) {
artView.setTransitionName("a"+addonDetails.addonid);
}
}
}
}

View File

@ -15,7 +15,6 @@
*/
package org.xbmc.kore.ui.sections.audio;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
@ -25,6 +24,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.support.v4.content.CursorLoader;
import android.text.TextUtils;
import android.view.LayoutInflater;
@ -33,7 +33,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
@ -48,6 +47,7 @@ import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
@ -162,8 +162,8 @@ public class AlbumListFragment extends AbstractCursorListFragment {
}
@Override
protected CursorAdapter createAdapter() {
return new AlbumsAdapter(getActivity());
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new AlbumsAdapter(this);
}
@Override
@ -263,113 +263,119 @@ public class AlbumListFragment extends AbstractCursorListFragment {
int RATING = 7;
}
private class AlbumsAdapter extends CursorAdapter {
private static class AlbumsAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
private Fragment fragment;
public AlbumsAdapter(Context context) {
super(context, null, 0);
this.hostManager = HostManager.getInstance(context);
public AlbumsAdapter(Fragment fragment) {
this.hostManager = HostManager.getInstance(fragment.getContext());
this.fragment = fragment;
// Get the art dimensions
// Use the same dimensions as in the details fragment, so that it hits Picasso's cache when
// the user transitions to that fragment, avoiding another call and imediatelly showing the image
Resources resources = context.getResources();
Resources resources = fragment.getContext().getResources();
artWidth = resources.getDimensionPixelOffset(R.dimen.detail_poster_width_square);
artHeight = resources.getDimensionPixelOffset(R.dimen.detail_poster_height_square);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_album, parent, false);
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(fragment.getContext())
.inflate(R.layout.grid_item_album, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
viewHolder.artistView = (TextView)view.findViewById(R.id.name);
viewHolder.genresView = (TextView)view.findViewById(R.id.genres);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
view.setTag(viewHolder);
return view;
return new ViewHolder(view, fragment.getContext(), hostManager, artWidth, artHeight,
albumlistItemMenuClickListener);
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
private View.OnClickListener albumlistItemMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
final ViewHolder viewHolder = (ViewHolder)v.getTag();
viewHolder.dataHolder.setId(cursor.getInt(AlbumListQuery.ALBUMID));
viewHolder.dataHolder.setTitle(cursor.getString(AlbumListQuery.TITLE));
viewHolder.dataHolder.setUndertitle(cursor.getString(AlbumListQuery.DISPLAYARTIST));
final PlaylistType.Item playListItem = new PlaylistType.Item();
playListItem.albumid = viewHolder.dataHolder.getId();
viewHolder.titleView.setText(viewHolder.dataHolder.getTitle());
viewHolder.artistView.setText(viewHolder.dataHolder.getUnderTitle());
int year = cursor.getInt(AlbumListQuery.YEAR);
String genres = cursor.getString(AlbumListQuery.GENRE);
String desc = (genres != null) ?
((year > 0) ? genres + " | " + year : genres) :
String.valueOf(year);
viewHolder.dataHolder.setDescription(desc);
viewHolder.genresView.setText(desc);
viewHolder.dataHolder.setPosterUrl(cursor.getString(AlbumListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
viewHolder.dataHolder.getPosterUrl(),
viewHolder.dataHolder.getTitle(),
viewHolder.artView, artWidth, artHeight);
// For the popupmenu
ImageView contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
contextMenu.setTag(viewHolder);
contextMenu.setOnClickListener(albumlistItemMenuClickListener);
if (Utils.isLollipopOrLater()) {
viewHolder.artView.setTransitionName("al"+viewHolder.dataHolder.getId());
final PopupMenu popupMenu = new PopupMenu(fragment.getContext(), v);
popupMenu.getMenuInflater().inflate(R.menu.musiclist_item, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_play:
MediaPlayerUtils.play(fragment, playListItem);
return true;
case R.id.action_queue:
MediaPlayerUtils.queue(fragment, playListItem, PlaylistType.GetPlaylistsReturnType.AUDIO);
return true;
}
return false;
}
});
popupMenu.show();
}
}
};
}
/**
* View holder pattern
*/
public static class ViewHolder {
public static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView titleView;
TextView artistView;
TextView genresView;
ImageView artView;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
}
private View.OnClickListener albumlistItemMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
final ViewHolder viewHolder = (ViewHolder)v.getTag();
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight,
View.OnClickListener contextMenuClickListener) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
titleView = itemView.findViewById(R.id.title);
artistView = itemView.findViewById(R.id.name);
genresView = itemView.findViewById(R.id.genres);
artView = itemView.findViewById(R.id.art);
final PlaylistType.Item playListItem = new PlaylistType.Item();
playListItem.albumid = viewHolder.dataHolder.getId();
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
popupMenu.getMenuInflater().inflate(R.menu.musiclist_item, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_play:
MediaPlayerUtils.play(AlbumListFragment.this, playListItem);
return true;
case R.id.action_queue:
MediaPlayerUtils.queue(AlbumListFragment.this, playListItem, PlaylistType.GetPlaylistsReturnType.AUDIO);
return true;
}
return false;
}
});
popupMenu.show();
// For the popupmenu
ImageView contextMenu = itemView.findViewById(R.id.list_context_menu);
contextMenu.setTag(this);
contextMenu.setOnClickListener(contextMenuClickListener);
}
};
@Override
public void bindView(Cursor cursor) {
dataHolder.setId(cursor.getInt(AlbumListQuery.ALBUMID));
dataHolder.setTitle(cursor.getString(AlbumListQuery.TITLE));
dataHolder.setUndertitle(cursor.getString(AlbumListQuery.DISPLAYARTIST));
titleView.setText(dataHolder.getTitle());
artistView.setText(dataHolder.getUnderTitle());
int year = cursor.getInt(AlbumListQuery.YEAR);
String genres = cursor.getString(AlbumListQuery.GENRE);
String desc = (genres != null) ?
((year > 0) ? genres + " | " + year : genres) :
String.valueOf(year);
dataHolder.setDescription(desc);
genresView.setText(desc);
dataHolder.setPosterUrl(cursor.getString(AlbumListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
dataHolder.getPosterUrl(),
dataHolder.getTitle(),
artView, artWidth, artHeight);
if (Utils.isLollipopOrLater()) {
artView.setTransitionName("al"+dataHolder.getId());
}
}
}
}

View File

@ -15,20 +15,19 @@
*/
package org.xbmc.kore.ui.sections.audio;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.support.v4.content.CursorLoader;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
@ -42,6 +41,7 @@ import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
@ -72,8 +72,8 @@ public class ArtistListFragment extends AbstractCursorListFragment {
}
@Override
protected CursorAdapter createAdapter() {
return new ArtistsAdapter(getActivity());
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new ArtistsAdapter(this);
}
@Override
@ -135,105 +135,108 @@ public class ArtistListFragment extends AbstractCursorListFragment {
int FANART = 6;
}
private class ArtistsAdapter extends CursorAdapter {
private static class ArtistsAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
Fragment fragment;
public ArtistsAdapter(Context context) {
super(context, null, 0);
this.hostManager = HostManager.getInstance(context);
public ArtistsAdapter(Fragment fragment) {
this.fragment = fragment;
this.hostManager = HostManager.getInstance(fragment.getContext());
// Get the art dimensions
Resources resources = context.getResources();
Resources resources = fragment.getContext().getResources();
artWidth = (int)(resources.getDimension(R.dimen.detail_poster_width_square));
artHeight = (int)(resources.getDimension(R.dimen.detail_poster_height_square));
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_artist, parent, false);
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(fragment.getContext())
.inflate(R.layout.grid_item_artist, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.nameView = (TextView)view.findViewById(R.id.name);
viewHolder.genresView = (TextView)view.findViewById(R.id.genres);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
viewHolder.contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
view.setTag(viewHolder);
return view;
return new ViewHolder(view, fragment.getContext(), hostManager, artWidth, artHeight, artistlistItemMenuClickListener);
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
private View.OnClickListener artistlistItemMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
final ViewHolder viewHolder = (ViewHolder)v.getTag();
// Save the movie id
viewHolder.dataHolder.setId(cursor.getInt(ArtistListQuery.ARTISTID));
viewHolder.dataHolder.setTitle(cursor.getString(ArtistListQuery.ARTIST));
viewHolder.dataHolder.setUndertitle(cursor.getString(ArtistListQuery.GENRE));
viewHolder.dataHolder.setDescription(cursor.getString(ArtistListQuery.DESCRIPTION));
viewHolder.dataHolder.setFanArtUrl(cursor.getString(ArtistListQuery.FANART));
final PlaylistType.Item playListItem = new PlaylistType.Item();
playListItem.artistid = viewHolder.dataHolder.getId();
viewHolder.nameView.setText(cursor.getString(ArtistListQuery.ARTIST));
viewHolder.genresView.setText(cursor.getString(ArtistListQuery.GENRE));
viewHolder.dataHolder.setPosterUrl(cursor.getString(ArtistListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
viewHolder.dataHolder.getPosterUrl(), viewHolder.dataHolder.getTitle(),
viewHolder.artView, artWidth, artHeight);
viewHolder.contextMenu.setTag(viewHolder);
viewHolder.contextMenu.setOnClickListener(artistlistItemMenuClickListener);
if (Utils.isLollipopOrLater()) {
viewHolder.artView.setTransitionName("ar"+viewHolder.dataHolder.getId());
final PopupMenu popupMenu = new PopupMenu(fragment.getContext(), v);
popupMenu.getMenuInflater().inflate(R.menu.musiclist_item, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_play:
MediaPlayerUtils.play(fragment, playListItem);
return true;
case R.id.action_queue:
MediaPlayerUtils.queue(fragment, playListItem, PlaylistType.GetPlaylistsReturnType.AUDIO);
return true;
}
return false;
}
});
popupMenu.show();
}
}
};
}
/**
* View holder pattern
*/
public static class ViewHolder {
public static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView nameView;
TextView genresView;
ImageView artView;
ImageView contextMenu;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
}
private View.OnClickListener artistlistItemMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
final ViewHolder viewHolder = (ViewHolder)v.getTag();
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight,
View.OnClickListener contextMenuClickListener) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
nameView = itemView.findViewById(R.id.name);
genresView = itemView.findViewById(R.id.genres);
artView = itemView.findViewById(R.id.art);
final PlaylistType.Item playListItem = new PlaylistType.Item();
playListItem.artistid = viewHolder.dataHolder.getId();
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
popupMenu.getMenuInflater().inflate(R.menu.musiclist_item, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_play:
MediaPlayerUtils.play(ArtistListFragment.this, playListItem);
return true;
case R.id.action_queue:
MediaPlayerUtils.queue(ArtistListFragment.this, playListItem, PlaylistType.GetPlaylistsReturnType.AUDIO);
return true;
}
return false;
}
});
popupMenu.show();
ImageView contextMenu = itemView.findViewById(R.id.list_context_menu);
contextMenu.setTag(this);
contextMenu.setOnClickListener(contextMenuClickListener);
}
};
@Override
public void bindView(Cursor cursor) {
dataHolder.setId(cursor.getInt(ArtistListQuery.ARTISTID));
dataHolder.setTitle(cursor.getString(ArtistListQuery.ARTIST));
dataHolder.setUndertitle(cursor.getString(ArtistListQuery.GENRE));
dataHolder.setDescription(cursor.getString(ArtistListQuery.DESCRIPTION));
dataHolder.setFanArtUrl(cursor.getString(ArtistListQuery.FANART));
nameView.setText(cursor.getString(ArtistListQuery.ARTIST));
genresView.setText(cursor.getString(ArtistListQuery.GENRE));
dataHolder.setPosterUrl(cursor.getString(ArtistListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
dataHolder.getPosterUrl(), dataHolder.getTitle(),
artView, artWidth, artHeight);
if (Utils.isLollipopOrLater()) {
artView.setTransitionName("ar"+dataHolder.getId());
}
}
}
}

View File

@ -27,7 +27,6 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
@ -39,6 +38,7 @@ import org.xbmc.kore.jsonrpc.type.PlaylistType;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
@ -68,7 +68,7 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
}
@Override
protected CursorAdapter createAdapter() {
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new AudioGenresAdapter(getActivity());
}
@ -125,13 +125,12 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
final int THUMBNAIL = 3;
}
private class AudioGenresAdapter extends CursorAdapter {
private class AudioGenresAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
public AudioGenresAdapter(Context context) {
super(context, null, 0);
this.hostManager = HostManager.getInstance(context);
// Get the art dimensions
@ -142,52 +141,56 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
UIUtils.IMAGE_RESIZE_FACTOR);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_audio_genre, parent, false);
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(getContext())
.inflate(R.layout.grid_item_audio_genre, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
view.setTag(viewHolder);
return view;
}
/** {@inheritDoc} */
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
viewHolder.genreId = cursor.getInt(AudioGenreListQuery.GENREID);
viewHolder.genreTitle = cursor.getString(AudioGenreListQuery.TITLE);
viewHolder.titleView.setText(viewHolder.genreTitle);
String thumbnail = cursor.getString(AudioGenreListQuery.THUMBNAIL);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
thumbnail, viewHolder.genreTitle,
viewHolder.artView, artWidth, artHeight);
// For the popupmenu
ImageView contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
contextMenu.setTag(viewHolder);
contextMenu.setOnClickListener(genrelistItemMenuClickListener);
return new ViewHolder(view, getContext(), hostManager, artWidth, artHeight, genrelistItemMenuClickListener);
}
}
/**
* View holder pattern
*/
private static class ViewHolder {
private static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView titleView;
ImageView artView;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
int genreId;
String genreTitle;
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight,
View.OnClickListener contextMenuClickListener) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
titleView = itemView.findViewById(R.id.title);
artView = itemView.findViewById(R.id.art);
ImageView contextMenu = itemView.findViewById(R.id.list_context_menu);
contextMenu.setTag(this);
contextMenu.setOnClickListener(contextMenuClickListener);
}
@Override
public void bindView(Cursor cursor) {
genreId = cursor.getInt(AudioGenreListQuery.GENREID);
genreTitle = cursor.getString(AudioGenreListQuery.TITLE);
titleView.setText(genreTitle);
String thumbnail = cursor.getString(AudioGenreListQuery.THUMBNAIL);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
thumbnail, genreTitle, artView, artWidth, artHeight);
}
}
private View.OnClickListener genrelistItemMenuClickListener = new View.OnClickListener() {

View File

@ -15,7 +15,6 @@
*/
package org.xbmc.kore.ui.sections.audio;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@ -27,7 +26,6 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
@ -39,6 +37,7 @@ import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
@ -68,7 +67,7 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
}
@Override
protected CursorAdapter createAdapter() {
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new MusicVideosAdapter(getActivity());
}
@ -137,13 +136,15 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
int PLOT = 9;
}
private static class MusicVideosAdapter extends CursorAdapter {
private static class MusicVideosAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
private Context context;
public MusicVideosAdapter(Context context) {
super(context, null, 0);
this.context = context;
this.hostManager = HostManager.getInstance(context);
// Get the art dimensions
@ -152,38 +153,50 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
artWidth = resources.getDimensionPixelOffset(R.dimen.detail_poster_height_square);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_music_video, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
viewHolder.artistAlbumView = (TextView)view.findViewById(R.id.details);
viewHolder.durationGenresView = (TextView)view.findViewById(R.id.duration);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
view.setTag(viewHolder);
return view;
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_music_video, parent, false);
return new ViewHolder(view, context, hostManager, artWidth, artHeight);
}
}
/** {@inheritDoc} */
@TargetApi(21)
/**
* View holder pattern
*/
public static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView titleView;
TextView artistAlbumView;
TextView durationGenresView;
ImageView artView;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
titleView = itemView.findViewById(R.id.title);
artistAlbumView = itemView.findViewById(R.id.details);
durationGenresView = itemView.findViewById(R.id.duration);
artView = itemView.findViewById(R.id.art);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
public void bindView(Cursor cursor) {
dataHolder.setId(cursor.getInt(MusicVideosListQuery.MUSICVIDEOID));
dataHolder.setTitle(cursor.getString(MusicVideosListQuery.TITLE));
// Save the movie id
viewHolder.dataHolder.setId(cursor.getInt(MusicVideosListQuery.MUSICVIDEOID));
viewHolder.dataHolder.setTitle(cursor.getString(MusicVideosListQuery.TITLE));
viewHolder.titleView.setText(viewHolder.dataHolder.getTitle());
titleView.setText(dataHolder.getTitle());
String artistAlbum = cursor.getString(MusicVideosListQuery.ARTIST) + " | " +
cursor.getString(MusicVideosListQuery.ALBUM);
viewHolder.artistAlbumView.setText(artistAlbum);
viewHolder.dataHolder.setUndertitle(artistAlbum);
artistAlbumView.setText(artistAlbum);
dataHolder.setUndertitle(artistAlbum);
int runtime = cursor.getInt(MusicVideosListQuery.RUNTIME);
String genres = cursor.getString(MusicVideosListQuery.GENRES);
@ -191,30 +204,17 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
runtime > 0 ?
UIUtils.formatTime(runtime) + " | " + genres :
genres;
viewHolder.durationGenresView.setText(durationGenres);
viewHolder.dataHolder.setDetails(durationGenres);
durationGenresView.setText(durationGenres);
dataHolder.setDetails(durationGenres);
String posterUrl = cursor.getString(MusicVideosListQuery.THUMBNAIL);
viewHolder.dataHolder.setPosterUrl(posterUrl);
dataHolder.setPosterUrl(posterUrl);
UIUtils.loadImageWithCharacterAvatar(context, hostManager, posterUrl
, viewHolder.dataHolder.getTitle(),
viewHolder.artView, artWidth, artHeight);
, dataHolder.getTitle(), artView, artWidth, artHeight);
if(Utils.isLollipopOrLater()) {
viewHolder.artView.setTransitionName("a"+viewHolder.dataHolder.getId());
artView.setTransitionName("a"+dataHolder.getId());
}
}
}
/**
* View holder pattern
*/
public static class ViewHolder {
TextView titleView;
TextView artistAlbumView;
TextView durationGenresView;
ImageView artView;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
}
}

View File

@ -34,7 +34,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
@ -48,6 +47,7 @@ import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.provider.MediaProvider;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.FileDownloadHelper;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
@ -67,7 +67,8 @@ public class SongsListFragment extends AbstractCursorListFragment {
private int artistId = -1;
private int albumId = -1;
private String albumTitle = "";
private static String albumTitle = "";
private Handler callbackHandler = new Handler();
@ -96,11 +97,21 @@ public class SongsListFragment extends AbstractCursorListFragment {
protected String getListSyncType() { return LibrarySyncService.SYNC_ALL_MUSIC; }
@Override
protected CursorAdapter createAdapter() {
protected RecyclerViewCursorAdapter createCursorAdapter() {
if (albumId != -1 ) {
return new AlbumSongsAdapter(getActivity());
return new AlbumSongsAdapter(getContext(), new View.OnClickListener() {
@Override
public void onClick(View v) {
showPopupMenu(v);
}
});
} else {
return new SongsAdapter(getActivity());
return new SongsAdapter(getContext(), new View.OnClickListener() {
@Override
public void onClick(View v) {
showPopupMenu(v);
}
});
}
}
@ -234,15 +245,17 @@ public class SongsListFragment extends AbstractCursorListFragment {
int DISC = 7;
}
private class SongsAdapter extends CursorAdapter {
private static class SongsAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
private Context context;
private View.OnClickListener contextMenuClickListener;
public SongsAdapter(Context context) {
super(context, null, 0);
public SongsAdapter(Context context, View.OnClickListener contextMenuClickListener) {
this.hostManager = HostManager.getInstance(context);
this.context = context;
this.contextMenuClickListener = contextMenuClickListener;
// Get the art dimensions
// Use the same dimensions as in the details fragment, so that it hits Picasso's cache when
// the user transitions to that fragment, avoiding another call and imediatelly showing the image
@ -251,46 +264,103 @@ public class SongsListFragment extends AbstractCursorListFragment {
artHeight = resources.getDimensionPixelOffset(R.dimen.detail_poster_height_square);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context)
.inflate(R.layout.grid_item_song, parent, false);
.inflate(R.layout.grid_item_song, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.title = (TextView)view.findViewById(R.id.title);
viewHolder.details = (TextView)view.findViewById(R.id.details);
viewHolder.art = (ImageView)view.findViewById(R.id.art);
viewHolder.artist = (TextView)view.findViewById(R.id.artist);
viewHolder.songInfo = new FileDownloadHelper.SongInfo();
viewHolder.contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
return new SongViewHolder(view, context, hostManager, artWidth, artHeight,
contextMenuClickListener);
}
}
view.setTag(viewHolder);
return view;
private static class AlbumSongsAdapter extends RecyclerViewCursorAdapter {
private Context context;
private View.OnClickListener contextMenuClickListener;
public AlbumSongsAdapter(Context context, View.OnClickListener contextMenuClickListener) {
this.context = context;
this.contextMenuClickListener = contextMenuClickListener;
}
/** {@inheritDoc} */
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context)
.inflate(R.layout.list_item_song, parent, false);
return new AlbumViewHolder(view, contextMenuClickListener);
}
}
/**
* View holder pattern
*/
abstract static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
ImageView art;
TextView detailsTextView;
TextView artistTextView;
ImageView contextMenu;
FileDownloadHelper.SongInfo songInfo;
ViewHolder(View itemView, View.OnClickListener contextMenuClickListener) {
super(itemView);
art = itemView.findViewById(R.id.art);
artistTextView = itemView.findViewById(R.id.artist);
detailsTextView = itemView.findViewById(R.id.details);
contextMenu = itemView.findViewById(R.id.list_context_menu);
songInfo = new FileDownloadHelper.SongInfo();
contextMenu.setTag(this);
contextMenu.setOnClickListener(contextMenuClickListener);
}
@Override
public void bindView(Cursor cursor) {
songInfo.songId = cursor.getInt(SongsListQuery.SONGID);
songInfo.title = cursor.getString(SongsListQuery.TITLE);
songInfo.fileName = cursor.getString(SongsListQuery.FILE);
songInfo.track = cursor.getInt(SongsListQuery.TRACK);
}
}
public static class SongViewHolder extends ViewHolder {
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
TextView titleTextView;
SongViewHolder(View itemView, Context context, HostManager hostManager,
int artWidth, int artHeight, View.OnClickListener contextMenuClickListener) {
super(itemView, contextMenuClickListener);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
titleTextView = itemView.findViewById(R.id.title);
}
@Override
public void bindView(Cursor cursor) {
super.bindView(cursor);
String title = cursor.getString(SongsListQuery.TITLE);
titleTextView.setText(title);
viewHolder.title.setText(title);
String artist = cursor.getString(SongsListQuery.SONGDISPLAYARTIST);
viewHolder.artist.setText(artist);
artistTextView.setText(cursor.getString(SongsListQuery.SONGDISPLAYARTIST));
String albumTitle = cursor.getString(SongsListQuery.ALBUMTITLE);
int year = cursor.getInt(SongsListQuery.YEAR);
if (year > 0) {
setDetails(viewHolder.details,
setDetails(detailsTextView,
albumTitle,
String.valueOf(year),
cursor.getString(SongsListQuery.GENRE));
} else {
setDetails(viewHolder.details,
setDetails(detailsTextView,
albumTitle,
cursor.getString(SongsListQuery.GENRE));
}
@ -298,101 +368,60 @@ public class SongsListFragment extends AbstractCursorListFragment {
String thumbnail = cursor.getString(SongsListQuery.THUMBNAIL);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
thumbnail, title,
viewHolder.art, artWidth, artHeight);
art, artWidth, artHeight);
}
viewHolder.songInfo.artist = artist;
viewHolder.songInfo.album = albumTitle;
viewHolder.songInfo.songId = cursor.getInt(SongsListQuery.SONGID);
viewHolder.songInfo.title = cursor.getString(SongsListQuery.TITLE);
viewHolder.songInfo.fileName = cursor.getString(SongsListQuery.FILE);
viewHolder.songInfo.track = cursor.getInt(SongsListQuery.TRACK);
private void setDetails(TextView textView, String... elements) {
if ((elements == null) || (elements.length < 1)) {
return;
}
// For the popupmenu
viewHolder.contextMenu.setTag(viewHolder);
viewHolder.contextMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
showPopupMenu(v);
}
});
ArrayList<String> details = new ArrayList<>();
for (int i = 0; i < elements.length; i++) {
if (!TextUtils.isEmpty(elements[i]))
details.add(elements[i]);
}
textView.setText(TextUtils.join(" | ", details.toArray()));
}
}
private class AlbumSongsAdapter extends CursorAdapter {
public static class AlbumViewHolder extends ViewHolder {
TextView trackNumberTextView;
TextView titleTextView;
public AlbumSongsAdapter(Context context) {
super(context, null, 0);
AlbumViewHolder(View itemView, View.OnClickListener contextMenuClickListener) {
super(itemView, contextMenuClickListener);
trackNumberTextView = itemView.findViewById(R.id.track_number);
titleTextView = itemView.findViewById(R.id.song_title);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
View view = LayoutInflater.from(context)
.inflate(R.layout.list_item_song, viewGroup, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.trackNumber = (TextView)view.findViewById(R.id.track_number);
viewHolder.title = (TextView)view.findViewById(R.id.song_title);
viewHolder.details = (TextView)view.findViewById(R.id.details);
viewHolder.contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
viewHolder.songInfo = new FileDownloadHelper.SongInfo();
public void bindView(Cursor cursor) {
super.bindView(cursor);
view.setTag(viewHolder);
trackNumberTextView.setText(String.valueOf(songInfo.track));
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
String artist = cursor.getString(AlbumSongsListQuery.ARTIST);
ViewHolder vh = (ViewHolder) view.getTag();
titleTextView.setText(cursor.getString(AlbumSongsListQuery.TITLE));
vh.title.setText(cursor.getString(AlbumSongsListQuery.TITLE));
vh.songInfo.artist = artist;
vh.songInfo.album = albumTitle;
vh.songInfo.songId = cursor.getInt(SongsListQuery.SONGID);
vh.songInfo.title = cursor.getString(SongsListQuery.TITLE);
vh.songInfo.fileName = cursor.getString(SongsListQuery.FILE);
vh.songInfo.track = cursor.getInt(SongsListQuery.TRACK);
vh.trackNumber.setText(String.valueOf(vh.songInfo.track));
songInfo.artist = artist;
songInfo.album = albumTitle;
String duration = UIUtils.formatTime(cursor.getInt(AlbumSongsListQuery.DURATION));
String detailsText = TextUtils.isEmpty(artist) ? duration : duration + " | " + artist;
vh.details.setText(detailsText);
vh.contextMenu.setTag(vh);
vh.contextMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
showPopupMenu(v);
}
});
detailsTextView.setText(detailsText);
}
}
/**
* View holder pattern
*/
public static class ViewHolder {
ImageView art;
TextView title;
TextView details;
TextView artist;
TextView trackNumber;
ImageView contextMenu;
FileDownloadHelper.SongInfo songInfo;
}
private void showPopupMenu(View v) {
final ViewHolder viewHolder = (ViewHolder) v.getTag();
final PlaylistType.Item playListItem = new PlaylistType.Item();
playListItem.songid = viewHolder.songInfo.songId;
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
final PopupMenu popupMenu = new PopupMenu(getContext(), v);
popupMenu.getMenuInflater().inflate(R.menu.song_item, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
@ -407,9 +436,9 @@ public class SongsListFragment extends AbstractCursorListFragment {
case R.id.download:
ArrayList<FileDownloadHelper.SongInfo> songInfoList = new ArrayList<>();
songInfoList.add(viewHolder.songInfo);
UIUtils.downloadSongs(getActivity(),
UIUtils.downloadSongs(getContext(),
songInfoList,
HostManager.getInstance(getActivity()).getHostInfo(),
HostManager.getInstance(getContext()).getHostInfo(),
callbackHandler);
}
return false;
@ -417,19 +446,4 @@ public class SongsListFragment extends AbstractCursorListFragment {
});
popupMenu.show();
}
private void setDetails(TextView textView, String... elements) {
if ((elements == null) || (elements.length < 1)) {
return;
}
ArrayList<String> details = new ArrayList<>();
for (int i = 0; i < elements.length; i++) {
if (!TextUtils.isEmpty(elements[i]))
details.add(elements[i]);
}
textView.setText(TextUtils.join(" | ", details.toArray()));
}
}

View File

@ -23,13 +23,11 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -43,14 +41,14 @@ import org.xbmc.kore.jsonrpc.method.GUI;
import org.xbmc.kore.jsonrpc.type.FavouriteType;
import org.xbmc.kore.jsonrpc.type.PlaylistType;
import org.xbmc.kore.ui.AbstractListFragment;
import org.xbmc.kore.ui.viewgroups.RecyclerViewEmptyViewSupport;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
public class FavouritesListFragment extends AbstractListFragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FavouritesListFragment";
@ -63,7 +61,7 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
}
@Override
protected AdapterView.OnItemClickListener createOnItemClickListener() {
protected RecyclerViewEmptyViewSupport.OnItemClickListener createOnItemClickListener() {
final ApiCallback<String> genericApiCallback = new ApiCallback<String>() {
@Override
public void onSuccess(String result) {
@ -75,9 +73,9 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
Toast.makeText(getActivity(), description, Toast.LENGTH_SHORT).show();
}
};
return new AdapterView.OnItemClickListener() {
return new RecyclerViewEmptyViewSupport.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
public void onItemClick(View view, int position) {
final FavouritesAdapter favouritesAdapter = (FavouritesAdapter) getAdapter();
final HostManager hostManager = HostManager.getInstance(getActivity());
@ -105,7 +103,7 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
}
@Override
protected BaseAdapter createAdapter() {
protected RecyclerView.Adapter createAdapter() {
return new FavouritesAdapter(getActivity(), HostManager.getInstance(getActivity()));
}
@ -126,7 +124,7 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
// To prevent the empty text from appearing on the first load, set it now
getEmptyView().setText(getString(R.string.no_channels_found_refresh));
setupFavouritesList(result.items);
((FavouritesAdapter) getAdapter()).setFavouriteItems(result.items);
hideRefreshAnimation();
}
@ -143,25 +141,15 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
}, callbackHandler);
}
/**
* Called to set the GridView with the favourites that are coming from the host.
*
* @param favourites the favourites list that is supplied to the GridView.
*/
private void setupFavouritesList(List<FavouriteType.DetailsFavourite> favourites) {
final FavouritesAdapter favouritesAdapter = (FavouritesAdapter) getAdapter();
favouritesAdapter.clear();
favouritesAdapter.addAll(favourites);
favouritesAdapter.notifyDataSetChanged();
}
private static class FavouritesAdapter extends ArrayAdapter<FavouriteType.DetailsFavourite> {
private static class FavouritesAdapter extends RecyclerView.Adapter {
private final HostManager hostManager;
private final int artWidth, artHeight;
private Context context;
private ArrayList<FavouriteType.DetailsFavourite> favouriteItems = new ArrayList<>();
FavouritesAdapter(@NonNull Context context, HostManager hostManager) {
super(context, R.layout.grid_item_channel);
this.context = context;
this.hostManager = hostManager;
Resources resources = context.getResources();
artWidth = (int) (resources.getDimension(R.dimen.channellist_art_width) /
@ -170,23 +158,59 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
UIUtils.IMAGE_RESIZE_FACTOR);
}
@NonNull
public void setFavouriteItems(List<FavouriteType.DetailsFavourite> favouriteItems) {
this.favouriteItems.clear();
this.favouriteItems.addAll(favouriteItems);
notifyDataSetChanged();
}
public FavouriteType.DetailsFavourite getItem(int position) {
return favouriteItems.get(position);
}
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.grid_item_channel,
parent, false);
final FavouriteItemViewHolder vh = new FavouriteItemViewHolder(convertView);
convertView.setTag(vh);
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.grid_item_channel,
parent, false);
return new ViewHolder(view, context, hostManager, artWidth, artHeight);
}
final FavouriteItemViewHolder vh = (FavouriteItemViewHolder) convertView.getTag();
final FavouriteType.DetailsFavourite favouriteDetail = getItem(position);
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).bindView(favouriteItems.get(position));
}
// We don't need the context menu here.
vh.contextMenu.setVisibility(View.GONE);
@Override
public int getItemCount() {
return favouriteItems.size();
}
}
vh.titleView.setText(favouriteDetail.title);
private static class ViewHolder extends RecyclerView.ViewHolder {
final ImageView artView;
final TextView titleView;
final TextView detailView;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
artView = itemView.findViewById(R.id.art);
titleView = itemView.findViewById(R.id.title);
detailView = itemView.findViewById(R.id.details);
View contextMenu = itemView.findViewById(R.id.list_context_menu);
contextMenu.setVisibility(View.GONE);
}
void bindView(FavouriteType.DetailsFavourite favouriteDetail) {
titleView.setText(favouriteDetail.title);
@StringRes final int typeRes;
switch (favouriteDetail.type) {
@ -202,27 +226,11 @@ public class FavouritesListFragment extends AbstractListFragment implements Swip
default:
typeRes = R.string.unknown;
}
vh.detailView.setText(typeRes);
detailView.setText(typeRes);
UIUtils.loadImageWithCharacterAvatar(getContext(), hostManager,
favouriteDetail.thumbnail, favouriteDetail.title,
vh.artView, artWidth, artHeight);
return convertView;
}
}
private static class FavouriteItemViewHolder {
final ImageView artView;
final ImageView contextMenu;
final TextView titleView;
final TextView detailView;
FavouriteItemViewHolder(View v) {
artView = ButterKnife.findById(v, R.id.art);
contextMenu = ButterKnife.findById(v, R.id.list_context_menu);
titleView = ButterKnife.findById(v, R.id.title);
detailView = ButterKnife.findById(v, R.id.details);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
favouriteDetail.thumbnail, favouriteDetail.title,
artView, artWidth, artHeight);
}
}
}

View File

@ -21,14 +21,12 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
@ -45,6 +43,7 @@ import org.xbmc.kore.jsonrpc.type.ListType;
import org.xbmc.kore.jsonrpc.type.PlayerType;
import org.xbmc.kore.jsonrpc.type.PlaylistType;
import org.xbmc.kore.ui.AbstractListFragment;
import org.xbmc.kore.ui.viewgroups.RecyclerViewEmptyViewSupport;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
@ -112,17 +111,17 @@ public class MediaFileListFragment extends AbstractListFragment {
}
@Override
protected AdapterView.OnItemClickListener createOnItemClickListener() {
return new AdapterView.OnItemClickListener() {
protected RecyclerViewEmptyViewSupport.OnItemClickListener createOnItemClickListener() {
return new RecyclerViewEmptyViewSupport.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
public void onItemClick(View view, int position) {
handleFileSelect(((MediaFileListAdapter) getAdapter()).getItem(position));
}
};
}
@Override
protected BaseAdapter createAdapter() {
protected RecyclerView.Adapter createAdapter() {
return new MediaFileListAdapter(getActivity(), R.layout.grid_item_file);
}
@ -211,7 +210,7 @@ public class MediaFileListFragment extends AbstractListFragment {
}
public boolean atRootDirectory() {
if (getAdapter().getCount() == 0)
if (getAdapter().getItemCount() == 0)
return true;
FileLocation fl = ((MediaFileListAdapter) getAdapter()).getItem(0);
if (fl == null)
@ -500,11 +499,11 @@ public class MediaFileListFragment extends AbstractListFragment {
}
private class MediaFileListAdapter extends BaseAdapter implements ListAdapter {
private class MediaFileListAdapter extends RecyclerView.Adapter {
Context ctx;
int resource;
List<FileLocation> fileLocationItems = null;
List<FileLocation> fileLocationItems;
int artWidth;
int artHeight;
@ -552,7 +551,7 @@ public class MediaFileListFragment extends AbstractListFragment {
}
};
public MediaFileListAdapter(Context context, int resource) {
MediaFileListAdapter(Context context, int resource) {
super();
this.ctx = context;
this.resource = resource;
@ -580,20 +579,10 @@ public class MediaFileListFragment extends AbstractListFragment {
public List<FileLocation> getFileItemList() {
if (fileLocationItems == null)
return new ArrayList<FileLocation>();
return new ArrayList<FileLocation>(fileLocationItems);
return new ArrayList<>();
return new ArrayList<>(fileLocationItems);
}
@Override
public int getCount() {
if (fileLocationItems == null) {
return 0;
} else {
return fileLocationItems.size();
}
}
@Override
public FileLocation getItem(int position) {
if (fileLocationItems == null) {
return null;
@ -602,74 +591,80 @@ public class MediaFileListFragment extends AbstractListFragment {
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(ctx)
.inflate(resource, parent, false);
return new ViewHolder(view, getContext(), hostManager, artWidth, artHeight, itemMenuClickListener);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
FileLocation fileLocation = this.getItem(position);
((ViewHolder) holder).bindView(fileLocation, position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getViewTypeCount () {
return 1;
}
/** {@inheritDoc} */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(ctx)
.inflate(resource, parent, false);
// Setup View holder pattern
viewHolder = new ViewHolder();
viewHolder.art = (ImageView) convertView.findViewById(R.id.art);
viewHolder.title = (TextView) convertView.findViewById(R.id.title);
viewHolder.details = (TextView) convertView.findViewById(R.id.details);
viewHolder.contextMenu = (ImageView) convertView.findViewById(R.id.list_context_menu);
viewHolder.sizeDuration = (TextView) convertView.findViewById(R.id.size_duration);
convertView.setTag(viewHolder);
}
viewHolder = (ViewHolder) convertView.getTag();
FileLocation fileLocation = this.getItem(position);
// if (fileLocation.isDirectory) {
// viewHolder.title.setText(fileLocation.title);
// viewHolder.details.setText("");
// } else {
// viewHolder.title.setText("");
// viewHolder.details.setText(fileLocation.title);
// }
viewHolder.title.setText(fileLocation.title);
viewHolder.details.setText(fileLocation.details);
viewHolder.sizeDuration.setText(fileLocation.sizeDuration);
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
fileLocation.artUrl, fileLocation.title,
viewHolder.art, artWidth, artHeight);
// For the popup menu
if (fileLocation.isDirectory) {
viewHolder.contextMenu.setVisibility(View.GONE);
public int getItemCount() {
if (fileLocationItems == null) {
return 0;
} else {
viewHolder.contextMenu.setVisibility(View.VISIBLE);
viewHolder.contextMenu.setTag(position);
viewHolder.contextMenu.setOnClickListener(itemMenuClickListener);
return fileLocationItems.size();
}
return convertView;
}
}
/**
* View holder pattern
*/
private static class ViewHolder {
private static class ViewHolder extends RecyclerView.ViewHolder {
ImageView art;
TextView title;
TextView details;
TextView sizeDuration;
ImageView contextMenu;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
ViewHolder(View itemView, Context context, HostManager hostManager, int artWidth, int artHeight,
View.OnClickListener itemMenuClickListener) {
super(itemView);
this.context = context;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
art = itemView.findViewById(R.id.art);
title = itemView.findViewById(R.id.title);
details = itemView.findViewById(R.id.details);
contextMenu = itemView.findViewById(R.id.list_context_menu);
sizeDuration = itemView.findViewById(R.id.size_duration);
contextMenu.setOnClickListener(itemMenuClickListener);
}
public void bindView(FileLocation fileLocation, int position) {
title.setText(fileLocation.title);
details.setText(fileLocation.details);
sizeDuration.setText(fileLocation.sizeDuration);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
fileLocation.artUrl, fileLocation.title,
art, artWidth, artHeight);
// For the popup menu
if (fileLocation.isDirectory) {
contextMenu.setVisibility(View.GONE);
} else {
contextMenu.setVisibility(View.VISIBLE);
contextMenu.setTag(position);
}
}
}
public static class FileLocation implements Parcelable {

View File

@ -15,7 +15,6 @@
*/
package org.xbmc.kore.ui.sections.video;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
@ -33,7 +32,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
@ -45,7 +43,8 @@ import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.AbstractFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
@ -63,7 +62,7 @@ public class MovieListFragment extends AbstractCursorListFragment {
// Activity listener
private OnMovieSelectedListener listenerActivity;
private boolean showWatchedStatus;
private static boolean showWatchedStatus;
@Override
protected String getListSyncType() { return LibrarySyncService.SYNC_ALL_MOVIES; }
@ -77,7 +76,7 @@ public class MovieListFragment extends AbstractCursorListFragment {
}
@Override
protected CursorAdapter createAdapter() {
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new MoviesAdapter(getActivity());
}
@ -310,15 +309,13 @@ public class MovieListFragment extends AbstractCursorListFragment {
int PLAYCOUNT = 9;
}
private class MoviesAdapter extends CursorAdapter {
private class MoviesAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
private int themeAccentColor;
public MoviesAdapter(Context context) {
super(context, null, 0);
MoviesAdapter(Context context) {
// Get the default accent color
Resources.Theme theme = context.getTheme();
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
@ -340,83 +337,90 @@ public class MovieListFragment extends AbstractCursorListFragment {
UIUtils.IMAGE_RESIZE_FACTOR);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(getContext())
.inflate(R.layout.grid_item_movie, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
viewHolder.detailsView = (TextView)view.findViewById(R.id.details);
viewHolder.durationView = (TextView)view.findViewById(R.id.duration);
viewHolder.checkmarkView = (ImageView)view.findViewById(R.id.checkmark);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
view.setTag(viewHolder);
return view;
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
// Save the movie id
viewHolder.dataHolder.setId(cursor.getInt(MovieListQuery.MOVIEID));
viewHolder.dataHolder.setTitle(cursor.getString(MovieListQuery.TITLE));
viewHolder.dataHolder.setUndertitle(cursor.getString(MovieListQuery.TAGLINE));
int movieYear = cursor.getInt(MovieListQuery.YEAR);
viewHolder.dataHolder.setRating(cursor.getDouble(MovieListQuery.RATING));
viewHolder.dataHolder.setMaxRating(10);
viewHolder.titleView.setText(viewHolder.dataHolder.getTitle());
String genres = cursor.getString(MovieListQuery.GENRES);
String details = TextUtils.isEmpty(viewHolder.dataHolder.getUnderTitle()) ?
genres : viewHolder.dataHolder.getUnderTitle();
viewHolder.detailsView.setText(details);
int runtime = cursor.getInt(MovieListQuery.RUNTIME) / 60;
String duration = runtime > 0 ?
String.format(context.getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
" | " + movieYear :
String.valueOf(movieYear);
viewHolder.durationView.setText(duration);
viewHolder.dataHolder.setDetails(duration + "\n" + details);
viewHolder.dataHolder.setPosterUrl(cursor.getString(MovieListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
viewHolder.dataHolder.getPosterUrl(),
viewHolder.dataHolder.getTitle(),
viewHolder.artView, artWidth, artHeight);
if (showWatchedStatus && (cursor.getInt(MovieListQuery.PLAYCOUNT) > 0)) {
viewHolder.checkmarkView.setVisibility(View.VISIBLE);
viewHolder.checkmarkView.setColorFilter(themeAccentColor);
} else {
viewHolder.checkmarkView.setVisibility(View.INVISIBLE);
}
if (Utils.isLollipopOrLater()) {
viewHolder.artView.setTransitionName("a" + viewHolder.dataHolder.getId());
}
return new ViewHolder(view, getContext(), themeAccentColor, hostManager, artWidth, artHeight);
}
}
/**
* View holder pattern
*/
public static class ViewHolder {
public static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView titleView;
TextView detailsView;
TextView durationView;
ImageView checkmarkView;
ImageView artView;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
int themeAccentColor;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
AbstractFragment.DataHolder dataHolder = new AbstractFragment.DataHolder(0);
ViewHolder(View itemView, Context context, int themeAccentColor,
HostManager hostManager,
int artWidth, int artHeight) {
super(itemView);
this.context = context;
this.themeAccentColor = themeAccentColor;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
titleView = itemView.findViewById(R.id.title);
detailsView = itemView.findViewById(R.id.details);
durationView = itemView.findViewById(R.id.duration);
checkmarkView = itemView.findViewById(R.id.checkmark);
artView = itemView.findViewById(R.id.art);
}
@Override
public void bindView(Cursor cursor) {
// Save the movie id
dataHolder.setId(cursor.getInt(MovieListQuery.MOVIEID));
dataHolder.setTitle(cursor.getString(MovieListQuery.TITLE));
dataHolder.setUndertitle(cursor.getString(MovieListQuery.TAGLINE));
int movieYear = cursor.getInt(MovieListQuery.YEAR);
dataHolder.setRating(cursor.getDouble(MovieListQuery.RATING));
dataHolder.setMaxRating(10);
titleView.setText(dataHolder.getTitle());
String genres = cursor.getString(MovieListQuery.GENRES);
String details = TextUtils.isEmpty(dataHolder.getUnderTitle()) ?
genres : dataHolder.getUnderTitle();
detailsView.setText(details);
int runtime = cursor.getInt(MovieListQuery.RUNTIME) / 60;
String duration = runtime > 0 ?
String.format(context.getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
" | " + movieYear :
String.valueOf(movieYear);
durationView.setText(duration);
dataHolder.setDetails(duration + "\n" + details);
dataHolder.setPosterUrl(cursor.getString(MovieListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
dataHolder.getPosterUrl(),
dataHolder.getTitle(),
artView, artWidth, artHeight);
if (showWatchedStatus && (cursor.getInt(MovieListQuery.PLAYCOUNT) > 0)) {
checkmarkView.setVisibility(View.VISIBLE);
checkmarkView.setColorFilter(themeAccentColor);
} else {
checkmarkView.setVisibility(View.INVISIBLE);
}
if (Utils.isLollipopOrLater()) {
artView.setTransitionName("a" + dataHolder.getId());
}
}
}
}

View File

@ -47,8 +47,8 @@ import org.xbmc.kore.utils.UIUtils;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
/**
@ -96,7 +96,7 @@ public class PVRChannelsListFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_pvr_list, container, false);
unbinder = ButterKnife.bind(this, root);
if (savedInstanceState != null) {

View File

@ -42,8 +42,8 @@ import org.xbmc.kore.utils.UIUtils;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
/**
@ -75,7 +75,7 @@ public class PVRRecordingsListFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_generic_media_list, container, false);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_pvr_list, container, false);
unbinder = ButterKnife.bind(this, root);
hostManager = HostManager.getInstance(getActivity());

View File

@ -33,7 +33,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
@ -46,7 +45,8 @@ import org.xbmc.kore.jsonrpc.type.PlaylistType;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.AbstractFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
@ -114,9 +114,8 @@ public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
listenerActivity.onEpisodeSelected(tvshowId, tag);
}
@Override
protected CursorAdapter createAdapter() {
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new SeasonsEpisodesAdapter(getActivity());
}
@ -137,7 +136,6 @@ public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
EpisodesListQuery.PROJECTION, selection.toString(), null, EpisodesListQuery.SORT);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@ -218,25 +216,23 @@ public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
}
private class SeasonsEpisodesAdapter extends CursorAdapter {
private class SeasonsEpisodesAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
private int themeAccentColor;
private HostManager hostManager;
private int artWidth;
private int artHeight;
public SeasonsEpisodesAdapter(Context context) {
super(context, null, 0);
SeasonsEpisodesAdapter(Context context) {
// Get the default accent color
Resources.Theme theme = context.getTheme();
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
R.attr.colorAccent
});
themeAccentColor = styledAttributes.getColor(styledAttributes.getIndex(0), getResources().getColor(R.color.accent_default));
styledAttributes.recycle();
this.hostManager = HostManager.getInstance(context);
hostManager = HostManager.getInstance(context);
// Get the art dimensions
Resources resources = context.getResources();
@ -246,77 +242,84 @@ public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
UIUtils.IMAGE_RESIZE_FACTOR);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
.inflate(R.layout.list_item_episode, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
viewHolder.detailsView = (TextView)view.findViewById(R.id.details);
viewHolder.episodenumberView = (TextView)view.findViewById(R.id.episode_number);
viewHolder.contextMenuView = (ImageView)view.findViewById(R.id.list_context_menu);
viewHolder.checkmarkView = (ImageView)view.findViewById(R.id.checkmark);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
view.setTag(viewHolder);
return view;
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
// Save the episode id
viewHolder.dataHolder.setId(cursor.getInt(EpisodesListQuery.EPISODEID));
viewHolder.dataHolder.setTitle(cursor.getString(EpisodesListQuery.TITLE));
viewHolder.episodenumberView.setText(
String.format(context.getString(R.string.episode_number),
cursor.getInt(EpisodesListQuery.EPISODE)));
int runtime = cursor.getInt(EpisodesListQuery.RUNTIME) / 60;
String duration = runtime > 0 ?
String.format(context.getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
" | " + cursor.getString(EpisodesListQuery.FIRSTAIRED) :
cursor.getString(EpisodesListQuery.FIRSTAIRED);
viewHolder.titleView.setText(cursor.getString(EpisodesListQuery.TITLE));
viewHolder.detailsView.setText(duration);
if (cursor.getInt(EpisodesListQuery.PLAYCOUNT) > 0) {
viewHolder.checkmarkView.setVisibility(View.VISIBLE);
viewHolder.checkmarkView.setColorFilter(themeAccentColor);
} else {
viewHolder.checkmarkView.setVisibility(View.INVISIBLE);
}
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
cursor.getString(EpisodesListQuery.THUMBNAIL),
viewHolder.dataHolder.getTitle(),
viewHolder.artView, artWidth, artHeight);
// For the popupmenu
ImageView contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
contextMenu.setTag(viewHolder);
contextMenu.setOnClickListener(contextlistItemMenuClickListener);
public RecyclerViewCursorAdapter.CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(getActivity())
.inflate(R.layout.list_item_episode, parent, false);
return new ViewHolder(view, getActivity(), themeAccentColor,
contextlistItemMenuClickListener, hostManager,
artWidth, artHeight);
}
}
/**
* View holder pattern, only for episodes
*/
public static class ViewHolder {
static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView titleView;
TextView detailsView;
TextView episodenumberView;
ImageView contextMenuView;
ImageView checkmarkView;
ImageView artView;
HostManager hostManager;
int artWidth;
int artHeight;
Context context;
int themeAccentColor;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
AbstractFragment.DataHolder dataHolder = new AbstractFragment.DataHolder(0);
ViewHolder(View itemView, Context context, int themeAccentColor,
View.OnClickListener contextlistItemMenuClickListener,
HostManager hostManager,
int artWidth, int artHeight) {
super(itemView);
this.context = context;
this.themeAccentColor = themeAccentColor;
this.hostManager = hostManager;
this.artWidth = artWidth;
this.artHeight = artHeight;
titleView = itemView.findViewById(R.id.title);
detailsView = itemView.findViewById(R.id.details);
episodenumberView = itemView.findViewById(R.id.episode_number);
contextMenuView = itemView.findViewById(R.id.list_context_menu);
checkmarkView = itemView.findViewById(R.id.checkmark);
artView = itemView.findViewById(R.id.art);
contextMenuView.setOnClickListener(contextlistItemMenuClickListener);
}
@Override
public void bindView(Cursor cursor) {
// Save the episode id
dataHolder.setId(cursor.getInt(EpisodesListQuery.EPISODEID));
dataHolder.setTitle(cursor.getString(EpisodesListQuery.TITLE));
episodenumberView.setText(
String.format(context.getString(R.string.episode_number),
cursor.getInt(EpisodesListQuery.EPISODE)));
int runtime = cursor.getInt(EpisodesListQuery.RUNTIME) / 60;
String duration = runtime > 0 ?
String.format(context.getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
" | " + cursor.getString(EpisodesListQuery.FIRSTAIRED) :
cursor.getString(EpisodesListQuery.FIRSTAIRED);
titleView.setText(cursor.getString(EpisodesListQuery.TITLE));
detailsView.setText(duration);
if (cursor.getInt(EpisodesListQuery.PLAYCOUNT) > 0) {
checkmarkView.setVisibility(View.VISIBLE);
checkmarkView.setColorFilter(themeAccentColor);
} else {
checkmarkView.setVisibility(View.INVISIBLE);
}
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
cursor.getString(EpisodesListQuery.THUMBNAIL),
dataHolder.getTitle(),
artView, artWidth, artHeight);
contextMenuView.setTag(this);
}
}
private View.OnClickListener contextlistItemMenuClickListener = new View.OnClickListener() {

View File

@ -15,7 +15,6 @@
*/
package org.xbmc.kore.ui.sections.video;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
@ -34,7 +33,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@ -48,6 +46,7 @@ import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
@ -65,7 +64,7 @@ public class TVShowListFragment extends AbstractCursorListFragment {
// Activity listener
private OnTVShowSelectedListener listenerActivity;
private boolean showWatchedStatus;
private static boolean showWatchedStatus;
@Override
protected String getListSyncType() { return LibrarySyncService.SYNC_ALL_TVSHOWS; }
@ -79,7 +78,7 @@ public class TVShowListFragment extends AbstractCursorListFragment {
}
@Override
protected CursorAdapter createAdapter() {
protected RecyclerViewCursorAdapter createCursorAdapter() {
return new TVShowsAdapter(getActivity());
}
@ -309,7 +308,7 @@ public class TVShowListFragment extends AbstractCursorListFragment {
int GENRES = 13;
}
private class TVShowsAdapter extends CursorAdapter {
private class TVShowsAdapter extends RecyclerViewCursorAdapter {
private HostManager hostManager;
private int artWidth, artHeight;
@ -317,8 +316,6 @@ public class TVShowListFragment extends AbstractCursorListFragment {
inProgressColor, finishedColor;
public TVShowsAdapter(Context context) {
super(context, null, 0);
// Get the default accent color
Resources.Theme theme = context.getTheme();
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
@ -344,82 +341,98 @@ public class TVShowListFragment extends AbstractCursorListFragment {
UIUtils.IMAGE_RESIZE_FACTOR);
}
/** {@inheritDoc} */
@Override
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
final View view = LayoutInflater.from(context)
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(getContext())
.inflate(R.layout.grid_item_tvshow, parent, false);
// Setup View holder pattern
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
viewHolder.detailsView = (TextView)view.findViewById(R.id.details);
viewHolder.premieredView = (TextView)view.findViewById(R.id.premiered);
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
viewHolder.watchedProgressView = (ProgressBar)view.findViewById(R.id.tv_shows_progress_bar);
ViewHolder viewHolder = new ViewHolder(view, getContext(),
themeAccentColor, inProgressColor, finishedColor,
hostManager,
artWidth, artHeight);
view.setTag(viewHolder);
return view;
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder vh = (ViewHolder)view.getTag();
// Save the movie id
vh.dataHolder.setId(cursor.getInt(TVShowListQuery.TVSHOWID));
vh.dataHolder.setTitle(cursor.getString(TVShowListQuery.TITLE));
vh.dataHolder.setDescription(cursor.getString(TVShowListQuery.PLOT));
vh.dataHolder.setRating(cursor.getInt(TVShowListQuery.RATING));
int numEpisodes = cursor.getInt(TVShowListQuery.EPISODE);
int watchedEpisodes = cursor.getInt(TVShowListQuery.WATCHEDEPISODES);
vh.titleView.setText(vh.dataHolder.getTitle());
String details = String.format(context.getString(R.string.num_episodes),
numEpisodes, numEpisodes - watchedEpisodes);
vh.detailsView.setText(details);
vh.dataHolder.setUndertitle(details);
String premiered = String.format(context.getString(R.string.premiered),
cursor.getString(TVShowListQuery.PREMIERED));
vh.premieredView.setText(premiered);
vh.dataHolder.setDetails(premiered);
vh.dataHolder.setPosterUrl(cursor.getString(TVShowListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
vh.dataHolder.getPosterUrl(),
vh.dataHolder.getTitle(),
vh.artView, artWidth, artHeight);
if (showWatchedStatus) {
vh.watchedProgressView.setVisibility(View.VISIBLE);
vh.watchedProgressView.setMax(numEpisodes);
vh.watchedProgressView.setProgress(watchedEpisodes);
} else {
vh.watchedProgressView.setVisibility(View.INVISIBLE);
}
if (Utils.isLollipopOrLater()) {
if (showWatchedStatus) {
int watchedColor = (numEpisodes - watchedEpisodes == 0)? finishedColor : inProgressColor;
vh.watchedProgressView.setProgressTintList(ColorStateList.valueOf(watchedColor));
}
vh.artView.setTransitionName("a" + vh.dataHolder.getId());
}
return viewHolder;
}
}
/**
* View holder pattern
*/
public static class ViewHolder {
public static class ViewHolder extends RecyclerViewCursorAdapter.CursorViewHolder {
TextView titleView;
TextView detailsView;
TextView premieredView;
ImageView artView;
ProgressBar watchedProgressView;
Context context;
int themeAccentColor, inProgressColor, finishedColor;
HostManager hostManager;
int artWidth;
int artHeight;
AbstractInfoFragment.DataHolder dataHolder = new AbstractInfoFragment.DataHolder(0);
ViewHolder(View itemView, Context context,
int themeAccentColor, int inProgressColor, int finishedColor,
HostManager hostManager,
int artWidth, int artHeight) {
super(itemView);
this.hostManager = hostManager;
this.context = context;
this.artHeight = artHeight;
this.artWidth = artWidth;
this.themeAccentColor = themeAccentColor;
this.inProgressColor = inProgressColor;
this.finishedColor = finishedColor;
titleView = itemView.findViewById(R.id.title);
detailsView = itemView.findViewById(R.id.details);
premieredView = itemView.findViewById(R.id.premiered);
artView = itemView.findViewById(R.id.art);
watchedProgressView = itemView.findViewById(R.id.tv_shows_progress_bar);
}
@Override
public void bindView(Cursor cursor) {
// Save the movie id
dataHolder.setId(cursor.getInt(TVShowListQuery.TVSHOWID));
dataHolder.setTitle(cursor.getString(TVShowListQuery.TITLE));
dataHolder.setDescription(cursor.getString(TVShowListQuery.PLOT));
dataHolder.setRating(cursor.getInt(TVShowListQuery.RATING));
int numEpisodes = cursor.getInt(TVShowListQuery.EPISODE);
int watchedEpisodes = cursor.getInt(TVShowListQuery.WATCHEDEPISODES);
titleView.setText(dataHolder.getTitle());
String details = String.format(context.getString(R.string.num_episodes),
numEpisodes, numEpisodes - watchedEpisodes);
detailsView.setText(details);
dataHolder.setUndertitle(details);
String premiered = String.format(context.getString(R.string.premiered),
cursor.getString(TVShowListQuery.PREMIERED));
premieredView.setText(premiered);
dataHolder.setDetails(premiered);
dataHolder.setPosterUrl(cursor.getString(TVShowListQuery.THUMBNAIL));
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
dataHolder.getPosterUrl(),
dataHolder.getTitle(),
artView, artWidth, artHeight);
if (showWatchedStatus) {
watchedProgressView.setVisibility(View.VISIBLE);
watchedProgressView.setMax(numEpisodes);
watchedProgressView.setProgress(watchedEpisodes);
} else {
watchedProgressView.setVisibility(View.INVISIBLE);
}
if (Utils.isLollipopOrLater()) {
if (showWatchedStatus) {
int watchedColor = (numEpisodes - watchedEpisodes == 0)? finishedColor : inProgressColor;
watchedProgressView.setProgressTintList(ColorStateList.valueOf(watchedColor));
}
artView.setTransitionName("a" + dataHolder.getId());
}
}
}
}

View File

@ -0,0 +1,168 @@
/*
* Copyright 2018 Martijn Brekhof. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbmc.kore.ui.viewgroups;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
/**
* <p>A Recycler view using a grid layout that supports auto sizing and showing an empty view when the adapter
* has no items.
* </p>
* <p>
* You can set the column width and column count using styleables:
* </p>
* <ul>
* <li>android:columnWidth=INTEGER</li>
* <li>android:columnCount=INTEGER</li>
* </ul>
*
* Inspired by <a href="http://blog.sqisland.com/2014/12/recyclerview-autofit-grid.html">RecyclerView: Autofit grid</a>
*/
public class RecyclerViewEmptyViewSupport extends RecyclerView {
public final static int AUTO_FIT = -1;
private View emptyView;
private OnItemClickListener onItemClickListener;
private int columnWidth;
private int columnCount = AUTO_FIT;
private GridLayoutManager gridLayoutManager;
private boolean multiColumnSupported;
public interface OnItemClickListener {
void onItemClick(View v, int position);
}
public RecyclerViewEmptyViewSupport(Context context) {
this(context, null);
}
public RecyclerViewEmptyViewSupport(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
@SuppressWarnings("ResourceType")
public RecyclerViewEmptyViewSupport(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setSaveEnabled(true);
if (attrs != null) {
int[] attrsArray = {
android.R.attr.columnWidth, android.R.attr.columnCount
};
TypedArray array = context.obtainStyledAttributes(
attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
columnCount = array.getInteger(1, AUTO_FIT);
array.recycle();
}
gridLayoutManager = new GridLayoutManager(getContext(), 1);
setLayoutManager(gridLayoutManager);
}
@Override
public void setAdapter(final Adapter adapter) {
super.setAdapter(adapter);
if (adapter == null)
return;
adapter.registerAdapterDataObserver(new AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
if (emptyView == null)
return;
if (adapter.getItemCount() == 0) {
emptyView.setVisibility(View.VISIBLE);
setVisibility(View.GONE);
} else {
emptyView.setVisibility(View.GONE);
setVisibility(View.VISIBLE);
}
}
});
}
@Override
public void onViewAdded(final View child) {
super.onViewAdded(child);
child.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(v, getChildAdapterPosition(child));
}
});
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
multiColumnSupported = spanCount > 1;
if (columnCount == AUTO_FIT) {
gridLayoutManager.setSpanCount(spanCount);
} else {
gridLayoutManager.setSpanCount(columnCount);
}
}
public boolean isMultiColumnSupported() {
return multiColumnSupported;
}
/**
* Sets the amount of columns.
* @param count amount of columns to use. Use {@link #AUTO_FIT}
* to calculate the amount based on available screen width
* and the specified column width
*/
public void setColumnCount(int count) {
columnCount = count;
invalidate();
}
public int getColumnCount() {
return columnCount;
}
public void setEmptyView(View emptyView) {
this.emptyView = emptyView;
}
public View getEmptyView() {
return emptyView;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}

View File

@ -27,19 +27,11 @@
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
<org.xbmc.kore.ui.viewgroups.RecyclerViewEmptyViewSupport
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/small_padding"
android:paddingRight="@dimen/small_padding"
android:paddingTop="@dimen/small_padding"
android:paddingBottom="@dimen/default_padding"
android:clipToPadding="false"
android:choiceMode="none"
android:listSelector="?attr/selectableItemBackground"
android:drawSelectorOnTop="true"
style="@style/Widget.GridView"/>
style="@style/GridLayoutRecyclerView"/>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015 Synced Synapse. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- TODO: replace gridview with RecyclerViewEmptyViewSupport -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/empty_view"/>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/small_padding"
android:paddingRight="@dimen/small_padding"
android:paddingTop="@dimen/small_padding"
android:paddingBottom="@dimen/default_padding"
android:clipToPadding="false"
android:choiceMode="none"
android:listSelector="?attr/selectableItemBackground"
android:drawSelectorOnTop="true"
style="@style/Widget.GridView"/>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<LinearLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<LinearLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<LinearLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -17,12 +17,10 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor"
style="@style/Widget.CardView">
android:focusable="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground">

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"

View File

@ -17,11 +17,9 @@
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardElevation="@dimen/default_card_elevation"
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
style="@style/Widget.CardView">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"

View File

@ -16,6 +16,8 @@
-->
<resources>
<!-- Default spacing because cardview on lollipop doesn't add padding -->
<dimen name="default_grid_horizontal_spacing">8dp</dimen>
<dimen name="default_grid_vertical_spacing">8dp</dimen>
<dimen name="default_grid_horizontal_spacing">4dp</dimen>
<dimen name="default_grid_vertical_spacing">4dp</dimen>
<dimen name="default_card_margin">8dp</dimen>
</resources>

View File

@ -37,6 +37,7 @@
<dimen name="card_corner_radius">2dp</dimen>
<dimen name="default_card_elevation">2dp</dimen>
<dimen name="default_card_margin">2dp</dimen>
<dimen name="text_size_small">12sp</dimen>
<dimen name="text_size_medium">14sp</dimen>

View File

@ -54,6 +54,21 @@
<item name="android:fastScrollEnabled">true</item>
</style>
<style name="GridLayoutRecyclerView">
<item name="android:clipToPadding">false</item>
<item name="android:scrollbars">vertical</item>
<item name="android:paddingBottom">@dimen/default_padding</item>
<item name="android:paddingLeft">@dimen/default_card_margin</item>
<item name="android:columnWidth">@dimen/default_grid_column_width</item>
</style>
<style name="Widget.CardView">
<item name="cardElevation">@dimen/default_card_elevation</item>
<item name="cardBackgroundColor">?attr/appCardBackgroundColor</item>
<item name="android:layout_marginRight">@dimen/default_card_margin</item>
<item name="android:layout_marginTop">@dimen/default_card_margin</item>
</style>
<style name="Widget.ListView">
<item name="android:fastScrollEnabled">true</item>
<item name="android:divider">@null</item>