From 3f5ccefe2e89c9552e266c1d416983edd79f487b Mon Sep 17 00:00:00 2001 From: Martijn Brekhof Date: Tue, 24 Jul 2018 22:04:33 +0200 Subject: [PATCH] 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 --- app/build.gradle | 1 + .../kore/ui/AbstractCursorListFragment.java | 20 +- .../xbmc/kore/ui/AbstractListFragment.java | 85 ++---- .../org/xbmc/kore/ui/BaseMediaActivity.java | 10 +- .../kore/ui/RecyclerViewCursorAdapter.java | 92 +++++++ .../ui/sections/addon/AddonListFragment.java | 140 ++++++---- .../ui/sections/audio/AlbumListFragment.java | 168 ++++++------ .../ui/sections/audio/ArtistListFragment.java | 153 +++++------ .../audio/AudioGenresListFragment.java | 79 +++--- .../audio/MusicVideoListFragment.java | 96 +++---- .../ui/sections/audio/SongsListFragment.java | 248 +++++++++--------- .../favourites/FavouritesListFragment.java | 122 +++++---- .../sections/file/MediaFileListFragment.java | 139 +++++----- .../ui/sections/video/MovieListFragment.java | 148 ++++++----- .../video/PVRChannelsListFragment.java | 4 +- .../video/PVRRecordingsListFragment.java | 4 +- .../video/TVShowEpisodeListFragment.java | 143 +++++----- .../ui/sections/video/TVShowListFragment.java | 145 +++++----- .../RecyclerViewEmptyViewSupport.java | 168 ++++++++++++ .../layout/fragment_generic_media_list.xml | 12 +- app/src/main/res/layout/fragment_pvr_list.xml | 47 ++++ app/src/main/res/layout/grid_item_addon.xml | 4 +- app/src/main/res/layout/grid_item_album.xml | 4 +- app/src/main/res/layout/grid_item_artist.xml | 4 +- .../main/res/layout/grid_item_audio_genre.xml | 4 +- app/src/main/res/layout/grid_item_channel.xml | 4 +- .../res/layout/grid_item_channel_group.xml | 4 +- app/src/main/res/layout/grid_item_file.xml | 4 +- app/src/main/res/layout/grid_item_host.xml | 4 +- app/src/main/res/layout/grid_item_movie.xml | 4 +- .../main/res/layout/grid_item_music_video.xml | 4 +- .../main/res/layout/grid_item_playlist.xml | 4 +- .../main/res/layout/grid_item_recording.xml | 4 +- app/src/main/res/layout/grid_item_season.xml | 4 +- app/src/main/res/layout/grid_item_song.xml | 4 +- app/src/main/res/layout/grid_item_tvshow.xml | 4 +- app/src/main/res/layout/list_item_episode.xml | 4 +- .../res/layout/list_item_next_episode.xml | 4 +- app/src/main/res/values-v21/dimens.xml | 6 +- app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/styles.xml | 15 ++ 41 files changed, 1223 insertions(+), 891 deletions(-) create mode 100644 app/src/main/java/org/xbmc/kore/ui/RecyclerViewCursorAdapter.java create mode 100644 app/src/main/java/org/xbmc/kore/ui/viewgroups/RecyclerViewEmptyViewSupport.java create mode 100644 app/src/main/res/layout/fragment_pvr_list.xml diff --git a/app/build.gradle b/app/build.gradle index 17db3fa..3fae257 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/org/xbmc/kore/ui/AbstractCursorListFragment.java b/app/src/main/java/org/xbmc/kore/ui/AbstractCursorListFragment.java index de352c8..ba87aed 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AbstractCursorListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AbstractCursorListFragment.java @@ -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 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 cursorLoader) { - ((CursorAdapter) getAdapter()).swapCursor(null); + ((RecyclerViewCursorAdapter) getAdapter()).swapCursor(null); } /** diff --git a/app/src/main/java/org/xbmc/kore/ui/AbstractListFragment.java b/app/src/main/java/org/xbmc/kore/ui/AbstractListFragment.java index 72f2e2b..ff2c4e9 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AbstractListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AbstractListFragment.java @@ -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; } diff --git a/app/src/main/java/org/xbmc/kore/ui/BaseMediaActivity.java b/app/src/main/java/org/xbmc/kore/ui/BaseMediaActivity.java index 88f97fe..1083857 100644 --- a/app/src/main/java/org/xbmc/kore/ui/BaseMediaActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/BaseMediaActivity.java @@ -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 { diff --git a/app/src/main/java/org/xbmc/kore/ui/RecyclerViewCursorAdapter.java b/app/src/main/java/org/xbmc/kore/ui/RecyclerViewCursorAdapter.java new file mode 100644 index 0000000..e2b74f4 --- /dev/null +++ b/app/src/main/java/org/xbmc/kore/ui/RecyclerViewCursorAdapter.java @@ -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 { + + 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); + } +} + diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonListFragment.java index 14e5296..c8f295c 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/addon/AddonListFragment.java @@ -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 { + 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 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); + } + } } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/audio/AlbumListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/audio/AlbumListFragment.java index 0eaa559..c35b395 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/audio/AlbumListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/audio/AlbumListFragment.java @@ -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()); + } + } + } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/audio/ArtistListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/audio/ArtistListFragment.java index 49e558a..20bf2cf 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/audio/ArtistListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/audio/ArtistListFragment.java @@ -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()); + } + } + } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/audio/AudioGenresListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/audio/AudioGenresListFragment.java index d0c3e6b..af83db9 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/audio/AudioGenresListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/audio/AudioGenresListFragment.java @@ -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() { diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/audio/MusicVideoListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/audio/MusicVideoListFragment.java index 44152c6..38ed4a9 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/audio/MusicVideoListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/audio/MusicVideoListFragment.java @@ -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); - } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/audio/SongsListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/audio/SongsListFragment.java index 99ea632..208352b 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/audio/SongsListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/audio/SongsListFragment.java @@ -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 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 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 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())); - } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/favourites/FavouritesListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/favourites/FavouritesListFragment.java index 4d2362c..b16b139 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/favourites/FavouritesListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/favourites/FavouritesListFragment.java @@ -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 genericApiCallback = new ApiCallback() { @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 favourites) { - final FavouritesAdapter favouritesAdapter = (FavouritesAdapter) getAdapter(); - favouritesAdapter.clear(); - favouritesAdapter.addAll(favourites); - favouritesAdapter.notifyDataSetChanged(); - } - - private static class FavouritesAdapter extends ArrayAdapter { + private static class FavouritesAdapter extends RecyclerView.Adapter { private final HostManager hostManager; private final int artWidth, artHeight; + private Context context; + private ArrayList 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 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); } } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/file/MediaFileListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/file/MediaFileListFragment.java index b2da5e6..d5fc7f4 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/file/MediaFileListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/file/MediaFileListFragment.java @@ -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 fileLocationItems = null; + List 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 getFileItemList() { if (fileLocationItems == null) - return new ArrayList(); - return new ArrayList(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 { diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/video/MovieListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/video/MovieListFragment.java index daef2eb..c0234b5 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/video/MovieListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/video/MovieListFragment.java @@ -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()); + } + } } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRChannelsListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRChannelsListFragment.java index 2a4d241..41a2ce1 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRChannelsListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRChannelsListFragment.java @@ -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) { diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRRecordingsListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRRecordingsListFragment.java index d9cf54c..cfbcd4d 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRRecordingsListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/video/PVRRecordingsListFragment.java @@ -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()); diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowEpisodeListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowEpisodeListFragment.java index 3eca31a..e8eb7b4 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowEpisodeListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowEpisodeListFragment.java @@ -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() { diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowListFragment.java b/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowListFragment.java index ecbeb71..aaa8e40 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/video/TVShowListFragment.java @@ -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()); + } + } } } diff --git a/app/src/main/java/org/xbmc/kore/ui/viewgroups/RecyclerViewEmptyViewSupport.java b/app/src/main/java/org/xbmc/kore/ui/viewgroups/RecyclerViewEmptyViewSupport.java new file mode 100644 index 0000000..b8505f7 --- /dev/null +++ b/app/src/main/java/org/xbmc/kore/ui/viewgroups/RecyclerViewEmptyViewSupport.java @@ -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; + +/** + *

A Recycler view using a grid layout that supports auto sizing and showing an empty view when the adapter + * has no items. + *

+ *

+ * You can set the column width and column count using styleables: + *

+ *
    + *
  • android:columnWidth=INTEGER
  • + *
  • android:columnCount=INTEGER
  • + *
+ * + * Inspired by RecyclerView: Autofit grid + */ +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; + } +} diff --git a/app/src/main/res/layout/fragment_generic_media_list.xml b/app/src/main/res/layout/fragment_generic_media_list.xml index 515d316..f1390e5 100644 --- a/app/src/main/res/layout/fragment_generic_media_list.xml +++ b/app/src/main/res/layout/fragment_generic_media_list.xml @@ -27,19 +27,11 @@ android:id="@+id/swipe_refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent"> - + style="@style/GridLayoutRecyclerView"/> diff --git a/app/src/main/res/layout/fragment_pvr_list.xml b/app/src/main/res/layout/fragment_pvr_list.xml new file mode 100644 index 0000000..5f2d869 --- /dev/null +++ b/app/src/main/res/layout/fragment_pvr_list.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/grid_item_addon.xml b/app/src/main/res/layout/grid_item_addon.xml index 9fb213e..4cd3890 100644 --- a/app/src/main/res/layout/grid_item_addon.xml +++ b/app/src/main/res/layout/grid_item_addon.xml @@ -17,11 +17,9 @@ + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> diff --git a/app/src/main/res/layout/grid_item_channel.xml b/app/src/main/res/layout/grid_item_channel.xml index 3b5ee11..ff8eb7b 100644 --- a/app/src/main/res/layout/grid_item_channel.xml +++ b/app/src/main/res/layout/grid_item_channel.xml @@ -17,11 +17,9 @@ + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> android:focusable="true" android:clickable="true" android:foreground="?android:attr/selectableItemBackground"> diff --git a/app/src/main/res/layout/grid_item_song.xml b/app/src/main/res/layout/grid_item_song.xml index 3fcf2dc..0f03870 100644 --- a/app/src/main/res/layout/grid_item_song.xml +++ b/app/src/main/res/layout/grid_item_song.xml @@ -17,11 +17,9 @@ + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> + style="@style/Widget.CardView"> - 8dp - 8dp + 4dp + 4dp + + 8dp \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a10ead7..3325859 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -37,6 +37,7 @@ 2dp 2dp + 2dp 12sp 14sp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5fb4866..acf9ca8 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -54,6 +54,21 @@ true + + + +