From 64a3b588a34c0158a5d7521697396d48ea833fb3 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 16 May 2015 16:00:00 -0700 Subject: #453 Move to using a RecyclerView for directories --- app/src/main/AndroidManifest.xml | 4 +- .../daneren2005/dsub/adapter/AlbumGridAdapter.java | 73 --- .../daneren2005/dsub/adapter/EntryAdapter.java | 2 +- .../daneren2005/dsub/adapter/EntryGridAdapter.java | 254 ++++++++++ .../dsub/adapter/EntryInfiniteGridAdapter.java | 95 ++++ .../dsub/fragments/SelectDirectoryFragment.java | 512 ++++++++------------- .../dsub/fragments/SubsonicFragment.java | 44 +- .../github/daneren2005/dsub/view/AlbumCell.java | 108 ----- .../github/daneren2005/dsub/view/AlbumView.java | 55 ++- .../dsub/view/GridSpacingDecoration.java | 99 ++++ .../github/daneren2005/dsub/view/SongView.java | 9 +- .../dsub/view/UnscrollableGridView.java | 128 ------ .../github/daneren2005/dsub/view/UpdateView.java | 35 +- app/src/main/res/layout/album_cell_item.xml | 5 +- app/src/main/res/layout/album_list_item.xml | 2 +- app/src/main/res/layout/grid_view.xml | 14 - app/src/main/res/layout/select_album.xml | 5 +- app/src/main/res/layout/song_list_item.xml | 2 +- app/src/main/res/layout/unscrollable_grid_view.xml | 11 - 19 files changed, 752 insertions(+), 705 deletions(-) delete mode 100644 app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java create mode 100644 app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java create mode 100644 app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java delete mode 100644 app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java create mode 100644 app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java delete mode 100644 app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java delete mode 100644 app/src/main/res/layout/grid_view.xml delete mode 100644 app/src/main/res/layout/unscrollable_grid_view.xml (limited to 'app/src/main') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 30a6eb3c..8eeaab2e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ - + diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java deleted file mode 100644 index eb187569..00000000 --- a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - This file is part of Subsonic. - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - Copyright 2014 (C) Scott Jackson -*/ - -package github.daneren2005.dsub.adapter; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; - -import java.util.List; - -import github.daneren2005.dsub.domain.MusicDirectory; -import github.daneren2005.dsub.util.ImageLoader; -import github.daneren2005.dsub.view.AlbumCell; - -public class AlbumGridAdapter extends ArrayAdapter { - private final static String TAG = AlbumGridAdapter.class.getSimpleName(); - private final Context activity; - private final ImageLoader imageLoader; - private List entries; - private boolean showArtist; - - public AlbumGridAdapter(Context activity, ImageLoader imageLoader, List entries, boolean showArtist) { - super(activity, android.R.layout.simple_list_item_1, entries); - this.entries = entries; - this.activity = activity; - this.imageLoader = imageLoader; - - // Always show artist if they aren't all the same - if(!showArtist) { - String artist = null; - for(MusicDirectory.Entry entry: entries) { - if(artist == null) { - artist = entry.getArtist(); - } - - if(artist != null && !artist.equals(entry.getArtist())) { - showArtist = true; - } - } - } - this.showArtist = showArtist; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - MusicDirectory.Entry entry = getItem(position); - - AlbumCell view; - if(convertView instanceof AlbumCell) { - view = (AlbumCell) convertView; - } else { - view = new AlbumCell(activity); - } - - view.setShowArtist(showArtist); - view.setObject(entry, imageLoader); - return view; - } -} diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java index 9e506e5a..9cc96ae0 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java @@ -60,7 +60,7 @@ public class EntryAdapter extends ArrayAdapter { if (entry.isDirectory()) { if(entry.isAlbum()) { AlbumView view; - view = new AlbumView(activity); + view = new AlbumView(activity, false); view.setObject(entry, imageLoader); return view; } else { diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java new file mode 100644 index 00000000..6de7ce33 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java @@ -0,0 +1,254 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.MusicDirectory.Entry; +import github.daneren2005.dsub.util.ImageLoader; +import github.daneren2005.dsub.view.AlbumView; +import github.daneren2005.dsub.view.SongView; +import github.daneren2005.dsub.view.UpdateView; +import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder; + +public class EntryGridAdapter extends RecyclerView.Adapter { + private static String TAG = EntryGridAdapter.class.getSimpleName(); + + public static int VIEW_TYPE_HEADER = 0; + public static int VIEW_TYPE_ALBUM_CELL = 1; + public static int VIEW_TYPE_ALBUM_LINE = 2; + public static int VIEW_TYPE_SONG = 3; + + protected Context context; + protected List entries; + private ImageLoader imageLoader; + private boolean largeAlbums; + private boolean showArtist = false; + private boolean checkable = true; + private OnEntryClickedListener onEntryClickedListener; + + private View header; + private List selected = new ArrayList(); + private UpdateView contextView; + private Entry contextEntry; + + public EntryGridAdapter(Context context, List entries, ImageLoader imageLoader, boolean largeCell) { + this.context = context; + this.entries = entries; + this.imageLoader = imageLoader; + this.largeAlbums = largeCell; + + // Always show artist if they aren't all the same + String artist = null; + for(MusicDirectory.Entry entry: entries) { + if(artist == null) { + artist = entry.getArtist(); + } + + if(artist != null && !artist.equals(entry.getArtist())) { + showArtist = true; + } + } + } + + @Override + public UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if(viewType == VIEW_TYPE_HEADER) { + return new UpdateViewHolder(header, false); + } + + UpdateView updateView = null; + if(viewType == VIEW_TYPE_ALBUM_LINE || viewType == VIEW_TYPE_ALBUM_CELL) { + updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL); + } else if(viewType == VIEW_TYPE_SONG) { + updateView = new SongView(context); + } + + if(viewType != VIEW_TYPE_HEADER && updateView != null) { + final UpdateView view = updateView; + updateView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Entry entry = getEntryForView(view); + + if (view.isCheckable() && v instanceof SongView) { + SongView songView = (SongView) v; + + if (selected.contains(entry)) { + selected.remove(entry); + songView.setChecked(false); + } else { + selected.add(entry); + songView.setChecked(true); + } + } else if (onEntryClickedListener != null) { + onEntryClickedListener.onEntryClicked(entry); + } + } + }); + updateView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + Entry entry = getEntryForView(view); + + setContextEntry(view, entry); + v.showContextMenu(); + return false; + } + }); + + View moreButton = updateView.findViewById(R.id.more_button); + if(moreButton != null) { + moreButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Entry entry = getEntryForView(view); + setContextEntry(view, entry); + v.showContextMenu(); + } + }); + } + } + + return new UpdateViewHolder(updateView); + } + + @Override + public void onBindViewHolder(UpdateViewHolder holder, int position) { + // Header already created + if(header != null && position == 0) { + return; + } + + UpdateView view = holder.getUpdateView(); + + int viewType = getItemViewType(position); + Entry entry = getEntryForPosition(position); + if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) { + AlbumView albumView = (AlbumView) view; + albumView.setShowArtist(showArtist); + albumView.setObject(entry, imageLoader); + } else if(viewType == VIEW_TYPE_SONG) { + SongView songView = (SongView) view; + songView.setObject(entry, checkable); + songView.setChecked(selected.contains(entry)); + } + view.setPosition(position); + } + + @Override + public int getItemCount() { + int size = entries.size(); + + if(header != null) { + size++; + } + + return size; + } + + @Override + public int getItemViewType(int position) { + if(header != null && position == 0) { + return VIEW_TYPE_HEADER; + } + + Entry entry = getEntryForPosition(position); + if(entry.isDirectory()) { + if (largeAlbums) { + return VIEW_TYPE_ALBUM_CELL; + } else { + return VIEW_TYPE_ALBUM_LINE; + } + } else { + return VIEW_TYPE_SONG; + } + } + + public Entry getEntryForView(UpdateView view) { + int position = view.getPosition(); + return getEntryForPosition(position); + } + public Entry getEntryForPosition(int position) { + if(header != null) { + position--; + } + + return entries.get(position); + } + + public void setHeader(View header) { + this.header = header; + } + + public void setShowArtist(boolean showArtist) { + this.showArtist = showArtist; + } + public void setCheckable(boolean checkable) { + this.checkable = checkable; + } + + public void setOnEntryClickedListener(OnEntryClickedListener listener) { + this.onEntryClickedListener = listener; + } + + public List getSelected() { + List selected = new ArrayList<>(); + selected.addAll(this.selected); + return selected; + } + public void clearSelected() { + for(Entry entry: selected) { + int index = entries.indexOf(entry); + this.notifyItemChanged(index); + } + selected.clear(); + } + + public void removeEntry(Entry entry) { + int index = entries.indexOf(entry); + if(index != -1) { + removeAt(index); + } + } + public void removeAt(int index) { + entries.remove(index); + notifyItemRemoved(index); + } + + public void setContextEntry(UpdateView view, Entry entry) { + this.contextView = view; + this.contextEntry = entry; + } + public UpdateView getContextView() { + return contextView; + } + public Entry getContextEntry() { + return contextEntry; + } + + public interface OnEntryClickedListener { + void onEntryClicked(Entry entry); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java new file mode 100644 index 00000000..3bc3f6aa --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java @@ -0,0 +1,95 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.adapter; + +import android.content.Context; + +import java.util.List; + +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.MusicDirectory.Entry; +import github.daneren2005.dsub.domain.ServerInfo; +import github.daneren2005.dsub.service.MusicService; +import github.daneren2005.dsub.service.MusicServiceFactory; +import github.daneren2005.dsub.util.ImageLoader; +import github.daneren2005.dsub.util.SilentBackgroundTask; + +public class EntryInfiniteGridAdapter extends EntryGridAdapter { + private String type; + private String extra; + private int size; + + private boolean loading = false; + private boolean allLoaded = false; + + public EntryInfiniteGridAdapter(Context context, List entries, ImageLoader imageLoader, boolean largeCell) { + super(context, entries, imageLoader, largeCell); + } + + public void setData(String type, String extra, int size) { + this.type = type; + this.extra = extra; + this.size = size; + } + + public void loadMore() { + if(loading || allLoaded) { + return; + } + loading = true; + + new SilentBackgroundTask(context) { + private List newData; + + @Override + protected Void doInBackground() throws Throwable { + newData = cacheInBackground(); + if(newData.isEmpty()) { + allLoaded = true; + } + return null; + } + + @Override + protected void done(Void result) { + appendCachedData(newData); + loading = false; + } + }.execute(); + } + + protected List cacheInBackground() throws Exception { + MusicService service = MusicServiceFactory.getMusicService(context); + MusicDirectory result; + int offset = entries.size(); + if(("genres".equals(type) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(type)) { + result = service.getAlbumList(type, extra, size, offset, context, null); + } else if("genres".equals(type) || "genres-songs".equals(type)) { + result = service.getSongsByGenre(extra, size, offset, context, null); + } else { + result = service.getAlbumList(type, size, offset, context, null); + } + return result.getChildren(); + } + + protected void appendCachedData(List newData) { + if(newData.size() > 0) { + int start = entries.size(); + entries.addAll(newData); + this.notifyItemRangeInserted(start, newData.size()); + } + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java index 9e7cd053..0a64e8fd 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -9,6 +9,9 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.text.Html; import android.text.SpannableString; import android.text.Spanned; @@ -23,24 +26,20 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.GridView; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.ListView; import android.widget.RatingBar; import android.widget.RelativeLayout; import android.widget.TextView; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.EntryInfiniteGridAdapter; +import github.daneren2005.dsub.adapter.EntryGridAdapter; import github.daneren2005.dsub.domain.ArtistInfo; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.ServerInfo; import github.daneren2005.dsub.domain.Share; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.util.ImageLoader; -import github.daneren2005.dsub.adapter.AlbumGridAdapter; -import github.daneren2005.dsub.adapter.EntryAdapter; import java.io.Serializable; import java.util.Iterator; @@ -58,9 +57,9 @@ import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.TabBackgroundTask; import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; -import github.daneren2005.dsub.adapter.AlbumListAdapter; -import github.daneren2005.dsub.view.HeaderGridView; +import github.daneren2005.dsub.view.GridSpacingDecoration; import github.daneren2005.dsub.view.MyLeadingMarginSpan2; +import github.daneren2005.dsub.view.UpdateView; import java.util.ArrayList; import java.util.Arrays; @@ -69,16 +68,14 @@ import java.util.Set; import static github.daneren2005.dsub.domain.MusicDirectory.Entry; -public class SelectDirectoryFragment extends SubsonicFragment implements AdapterView.OnItemClickListener { +public class SelectDirectoryFragment extends SubsonicFragment implements EntryGridAdapter.OnEntryClickedListener { private static final String TAG = SelectDirectoryFragment.class.getSimpleName(); - private GridView albumList; - private ListView entryList; + private RecyclerView recyclerView; + private EntryGridAdapter entryGridAdapter; private Boolean licenseValid; - private EntryAdapter entryAdapter; private List albums; private List entries; - private boolean albumContext = false; private boolean addAlbumHeader = false; private LoadTask currentTask; private ArtistInfo artistInfo; @@ -113,7 +110,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter super.onCreate(bundle); if(bundle != null) { entries = (List) bundle.getSerializable(Constants.FRAGMENT_LIST); - albums = (List) bundle.getSerializable(Constants.FRAGMENT_LIST2); artistInfo = (ArtistInfo) bundle.getSerializable(Constants.FRAGMENT_EXTRA); restoredInstance = true; } @@ -171,28 +167,40 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout); refreshLayout.setOnRefreshListener(this); - entryList = (ListView) rootView.findViewById(R.id.select_album_entries); - entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - entryList.setOnItemClickListener(this); - setupScrollList(entryList); - if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) { largeAlbums = true; } - if(albumListType == null || "starred".equals(albumListType) || !largeAlbums) { - albumList = (GridView) inflater.inflate(R.layout.unscrollable_grid_view, entryList, false); - addAlbumHeader = true; + recyclerView = (RecyclerView) rootView.findViewById(R.id.select_album_entries); + recyclerView.setHasFixedSize(true); + setupScrollList(recyclerView); + + if(largeAlbums) { + final int columns = context.getResources().getInteger(R.integer.Grid_Columns); + GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns); + gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + int viewType = entryGridAdapter.getItemViewType(position); + if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER) { + return columns; + } else { + return 1; + } + } + }); + recyclerView.addItemDecoration(new GridSpacingDecoration()); + recyclerView.setLayoutManager(gridLayoutManager); } else { - ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout); - albumList = (GridView) inflater.inflate(R.layout.grid_view, rootGroup, false); - rootGroup.removeView(entryList); - rootGroup.addView(albumList); + LinearLayoutManager layoutManager = new LinearLayoutManager(context); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + recyclerView.setLayoutManager(layoutManager); + } - setupScrollList(albumList); + if(albumListType == null || "starred".equals(albumListType)) { + addAlbumHeader = true; } - registerForContextMenu(entryList); - setupAlbumList(); + registerForContextMenu(recyclerView); if(entries == null) { if(primaryFragment || secondaryFragment) { @@ -286,21 +294,22 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter return true; case R.id.menu_download: downloadBackground(false); - selectAll(false, false); + entryGridAdapter.clearSelected(); return true; case R.id.menu_cache: downloadBackground(true); - selectAll(false, false); + entryGridAdapter.clearSelected(); return true; case R.id.menu_delete: delete(); - selectAll(false, false); + entryGridAdapter.clearSelected(); return true; case R.id.menu_add_playlist: - if(getSelectedSongs().isEmpty()) { - selectAll(true, false); + List songs = getSelectedSongs(); + if(songs.isEmpty()) { + songs = entries; } - addToPlaylist(getSelectedSongs()); + addToPlaylist(songs); return true; case R.id.menu_remove_playlist: removeFromPlaylist(playlistId, playlistName, getSelectedIndexes()); @@ -332,23 +341,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - - Entry entry; - if(view.getId() == R.id.select_album_entries) { - if(info.position == 0) { - return; - } - entry = (Entry) entryList.getItemAtPosition(info.position); - // When List has Grid embedded in header, this is called against the header as well - if(entry != null) { - albumContext = false; - } - } else { - entry = (Entry) albumList.getItemAtPosition(info.position); - albumContext = true; - } + Entry entry = entryGridAdapter.getContextEntry(); + UpdateView targetView = entryGridAdapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); // Don't try to display a context menu if error here if(entry == null) { @@ -383,22 +378,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter if(menuItem.getGroupId() != getSupportTag()) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Object selectedItem; - int headers = entryList.getHeaderViewsCount(); - if(albumContext) { - selectedItem = albumList.getItemAtPosition(info.position); - } else { - if(info.position == 0) { - return false; - } - selectedItem = entries.get(info.position - headers); - } + Entry entry = entryGridAdapter.getContextEntry(); if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) { List songs = new ArrayList(); - Iterator it = entries.listIterator(info.position - headers); + songs.add(entry); + Iterator it = entries.listIterator(entries.indexOf(entry)); while(it.hasNext()) { songs.add((Entry) it.next()); } @@ -407,19 +392,19 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter return true; } - if(onContextItemSelected(menuItem, selectedItem)) { + if(onContextItemSelected(menuItem, entry)) { return true; } switch (menuItem.getItemId()) { case R.id.song_menu_remove_playlist: - removeFromPlaylist(playlistId, playlistName, Arrays.asList(info.position - headers)); + removeFromPlaylist(playlistId, playlistName, Arrays.asList(entries.indexOf(entry))); break; case R.id.song_menu_server_download: - downloadPodcastEpisode((PodcastEpisode)selectedItem); + downloadPodcastEpisode((PodcastEpisode) entry); break; case R.id.song_menu_server_delete: - deletePodcastEpisode((PodcastEpisode)selectedItem); + deletePodcastEpisode((PodcastEpisode) entry); break; } @@ -427,38 +412,35 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (position >= 0) { - Entry entry = (Entry) parent.getItemAtPosition(position); - if (entry.isDirectory()) { - SubsonicFragment fragment = new SelectDirectoryFragment(); - Bundle args = new Bundle(); - args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId()); - args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle()); - args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry); - if ("newest".equals(albumListType)) { - args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true); - } - if(entry.getArtist() == null && entry.getParent() == null) { - args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true); - } - fragment.setArguments(args); - - replaceFragment(fragment, true); - } else if (entry.isVideo()) { - playVideo(entry); - } else if(entry instanceof PodcastEpisode) { - String status = ((PodcastEpisode)entry).getStatus(); - if("error".equals(status)) { - Util.toast(context, R.string.select_podcasts_error); - return; - } else if(!"completed".equals(status)) { - Util.toast(context, R.string.select_podcasts_skipped); - return; - } - - playNow(Arrays.asList(entry)); + public void onEntryClicked(Entry entry) { + if (entry.isDirectory()) { + SubsonicFragment fragment = new SelectDirectoryFragment(); + Bundle args = new Bundle(); + args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId()); + args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle()); + args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry); + if ("newest".equals(albumListType)) { + args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true); + } + if(entry.getArtist() == null && entry.getParent() == null) { + args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true); + } + fragment.setArguments(args); + + replaceFragment(fragment, true); + } else if (entry.isVideo()) { + playVideo(entry); + } else if(entry instanceof PodcastEpisode) { + String status = ((PodcastEpisode)entry).getStatus(); + if("error".equals(status)) { + Util.toast(context, R.string.select_podcasts_error); + return; + } else if(!"completed".equals(status)) { + Util.toast(context, R.string.select_podcasts_skipped); + return; } + + playNow(Arrays.asList(entry)); } } @@ -477,8 +459,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter if(currentTask != null) { currentTask.cancel(); } - - entryList.setVisibility(View.INVISIBLE); + + recyclerView.setVisibility(View.INVISIBLE); if (playlistId != null) { getPlaylist(playlistId, playlistName, refresh); } else if(podcastId != null) { @@ -685,11 +667,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter licenseValid = musicService.isLicenseValid(context, this); albums = dir.getChildren(true, false); - if(largeAlbums) { - entries = dir.getChildren(false, true); - } else { - entries = dir.getChildren(); - } + entries = dir.getChildren(); // This isn't really an artist if no albums on it! if(albums.size() == 0) { @@ -727,119 +705,112 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } private void finishLoading() { - // Show header if not album list type and not root and not artist - // For Subsonic 5.1+ display a header for artists with getArtistInfo data if it exists - View header = null; - if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) { - header = createHeader(); + boolean validData = !entries.isEmpty() || !albums.isEmpty(); + if(!validData) { + setEmpty(true); + } - if(header != null && artistInfoDelayed != null) { - final View finalHeader = header.findViewById(R.id.select_album_header); - final View headerProgress = header.findViewById(R.id.header_progress); + if(validData) { + recyclerView.setVisibility(View.VISIBLE); + } - finalHeader.setVisibility(View.INVISIBLE); - headerProgress.setVisibility(View.VISIBLE); + if(albumListType == null || "starred".equals(albumListType)) { + entryGridAdapter = new EntryGridAdapter(context, entries, getImageLoader(), largeAlbums); + } else { + entryGridAdapter = new EntryInfiniteGridAdapter(context, entries, getImageLoader(), largeAlbums); - new SilentBackgroundTask(context) { - @Override - protected Void doInBackground() throws Throwable { - MusicService musicService = MusicServiceFactory.getMusicService(context); - artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this); + // Setup infinite loading based on scrolling + final EntryInfiniteGridAdapter infiniteGridAdapter = (EntryInfiniteGridAdapter) entryGridAdapter; + infiniteGridAdapter.setData(albumListType, albumListExtra, albumListSize); - return null; - } + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } - @Override - protected void done(Void result) { - /*if(albumList instanceof HeaderGridView) { - HeaderGridView headerGridView = (HeaderGridView) albumList; - headerGridView.invalidateRowHeight(); - ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged(); - }*/ - - setupCoverArt(finalHeader); - setupTextDisplay(finalHeader); - setupButtonEvents(finalHeader); - - finalHeader.setVisibility(View.VISIBLE); - headerProgress.setVisibility(View.GONE); + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + int totalItemCount = layoutManager.getItemCount(); + int lastVisibleItem; + if(layoutManager instanceof GridLayoutManager) { + lastVisibleItem = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); + } else if(layoutManager instanceof LinearLayoutManager) { + lastVisibleItem = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); + } else { + return; } - }.execute(); - } - - // Only add header to entry list if we aren't going recreate album grid as root anyways - if(header != null && entryList != null && (!addAlbumHeader || entries.size() > 0)) { - entryList.addHeaderView(header, null, false); - header = null; - } - } - // Needs to be added here, GB crashes if you to try to remove the header view before adapter is set - if(addAlbumHeader) { - if(entries.size() > 0 || playlistId != null || podcastId != null) { - entryList.addHeaderView(albumList); - } else { - ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout); - albumList = (GridView) context.getLayoutInflater().inflate(R.layout.grid_view, rootGroup, false); - rootGroup.removeView(entryList); - rootGroup.addView(albumList); - - setupScrollList(albumList); - setupAlbumList(); - - // This should only not be null for a artist with only albums - if(header != null) { - HeaderGridView headerGridView = (HeaderGridView) albumList; - headerGridView.addHeaderView(header); + if(totalItemCount > 0 && lastVisibleItem >= totalItemCount - 1) { + infiniteGridAdapter.loadMore(); + } } - } - addAlbumHeader = false; + }); } - - boolean validData = !entries.isEmpty() || !albums.isEmpty(); - if(!validData) { - setEmpty(true); + entryGridAdapter.setOnEntryClickedListener(this); + // Always show artist if this is not a artist we are viewing + if(!artist) { + entryGridAdapter.setShowArtist(true); + } + // Podcasts are not checkable + if(podcastId != null) { + entryGridAdapter.setCheckable(false); } - // Always going to have entries in entryAdapter - entryAdapter = new EntryAdapter(context, getImageLoader(), entries, (podcastId == null)); - ListAdapter listAdapter = entryAdapter; - // Song-only genre needs to always be entry list + infinite adapter - if("genres-songs".equals(albumListType)) { - ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout); - if(rootGroup.findViewById(R.id.gridview) != null && largeAlbums) { - rootGroup.removeView(albumList); - rootGroup.addView(entryList); - } - listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize); - } else if(albumListType == null || "starred".equals(albumListType)) { - // Only set standard album adapter if not album list and largeAlbums is true - if(largeAlbums) { - albumList.setAdapter(new AlbumGridAdapter(context, getImageLoader(), albums, !artist)); - } - } else { - // If album list, use infinite adapters for either depending on whether or not largeAlbums is true - if(largeAlbums) { - albumList.setAdapter(new AlbumListAdapter(context, new AlbumGridAdapter(context, getImageLoader(), albums, true), albumListType, albumListExtra, albumListSize)); - } else { - listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize); + // Show header if not album list type and not root and not artist + // For Subsonic 5.1+ display a header for artists with getArtistInfo data if it exists + if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) { + View header = createHeader(); + + if(header != null) { + if (artistInfoDelayed != null) { + final View finalHeader = header.findViewById(R.id.select_album_header); + final View headerProgress = header.findViewById(R.id.header_progress); + + finalHeader.setVisibility(View.INVISIBLE); + headerProgress.setVisibility(View.VISIBLE); + + new SilentBackgroundTask(context) { + @Override + protected Void doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(context); + artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this); + + return null; + } + + @Override + protected void done(Void result) { + setupCoverArt(finalHeader); + setupTextDisplay(finalHeader); + setupButtonEvents(finalHeader); + + finalHeader.setVisibility(View.VISIBLE); + headerProgress.setVisibility(View.GONE); + } + }.execute(); + } + + entryGridAdapter.setHeader(header); } } - entryList.setAdapter(listAdapter); - if(validData) { - entryList.setVisibility(View.VISIBLE); - } + + recyclerView.setAdapter(entryGridAdapter); context.supportInvalidateOptionsMenu(); - if(lookupEntry != null) { + /*if(lookupEntry != null) { for(int i = 0; i < entries.size(); i++) { if(lookupEntry.equals(entries.get(i).getTitle())) { + recyclerView.scrollToPosition(); entryList.setSelection(i + entryList.getHeaderViewsCount()); lookupEntry = null; break; } } - } + }*/ Bundle args = getArguments(); boolean playAll = args.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); @@ -848,122 +819,51 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } } - private void setupAlbumList() { - albumList.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Entry entry = (Entry) parent.getItemAtPosition(position); - SubsonicFragment fragment = new SelectDirectoryFragment(); - Bundle args = new Bundle(); - args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId()); - args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle()); - args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry); - if ("newest".equals(albumListType)) { - args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true); - } - if(entry.getArtist() == null && entry.getParent() == null) { - args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true); - } - fragment.setArguments(args); - - replaceFragment(fragment, true); - } - }); - - registerForContextMenu(entryList); - registerForContextMenu(albumList); - } - private void playNow(final boolean shuffle, final boolean append) { playNow(shuffle, append, false); } private void playNow(final boolean shuffle, final boolean append, final boolean playNext) { - if(getSelectedSongs().size() > 0) { - download(append, false, !append, playNext, shuffle); - selectAll(false, false); + List songs = getSelectedSongs(); + if(!songs.isEmpty()) { + download(songs, append, false, !append, playNext, shuffle); + entryGridAdapter.clearSelected(); } else { playAll(shuffle, append); } } private void playAll(final boolean shuffle, final boolean append) { - boolean hasSubFolders = false; - for (int i = 0; i < entryList.getCount(); i++) { - Entry entry = (Entry) entryList.getItemAtPosition(i); - if (entry != null && entry.isDirectory()) { - hasSubFolders = true; - break; - } - } - if(albums.size() > 0) { - hasSubFolders = true; - } + boolean hasSubFolders = !albums.isEmpty(); if (hasSubFolders && (id != null || share != null || "starred".equals(albumListType))) { downloadRecursively(id, false, append, !append, shuffle, false); } else if(hasSubFolders && albumListType != null) { downloadRecursively(albums, shuffle, append); } else { - selectAll(true, false); - download(append, false, !append, false, shuffle); - selectAll(false, false); - } - } - - private void selectAll(boolean selected, boolean toast) { - int count = entryList.getCount(); - int selectedCount = 0; - for (int i = 0; i < count; i++) { - Entry entry = (Entry) entryList.getItemAtPosition(i); - if (entry != null && !entry.isDirectory() && !entry.isVideo()) { - entryList.setItemChecked(i, selected); - selectedCount++; - } - } - - // Display toast: N tracks selected / N tracks unselected - if (toast) { - int toastResId = selected ? R.string.select_album_n_selected - : R.string.select_album_n_unselected; - Util.toast(context, context.getString(toastResId, selectedCount)); + download(entries, append, false, !append, false, shuffle); } } private List getSelectedSongs() { - List songs = new ArrayList(10); - int count = entryList.getCount(); - for (int i = 0; i < count; i++) { - if (entryList.isItemChecked(i)) { - Entry entry = (Entry) entryList.getItemAtPosition(i); - // Don't try to add directories or 1-starred songs - if(!entry.isDirectory() && entry.getRating() != 1) { - songs.add(entry); - } - } - } - return songs; + return entryGridAdapter.getSelected(); } private List getSelectedIndexes() { + List selected = entryGridAdapter.getSelected(); List indexes = new ArrayList(); - int count = entryList.getCount(); - int headers = entryList.getHeaderViewsCount(); - for (int i = 0; i < count; i++) { - if (entryList.isItemChecked(i)) { - indexes.add(i - headers); - } + for(Entry entry: selected) { + indexes.add(entries.indexOf(entry)); } return indexes; } - private void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle) { + private void download(final List songs, final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle) { if (getDownloadService() == null) { return; } - final List songs = getSelectedSongs(); warnIfStorageUnavailable(); // Conditions for using play now button @@ -1006,11 +906,11 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter checkLicenseAndTrialPeriod(onValid); } private void downloadBackground(final boolean save) { + List songs = getSelectedSongs(); if(playlistId != null) { - selectAll(true, false); + songs = entries; } - List songs = getSelectedSongs(); if(songs.isEmpty()) { // Get both songs and albums downloadRecursively(id, save, false, false, false, true); @@ -1043,12 +943,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter private void delete() { List songs = getSelectedSongs(); if(songs.isEmpty()) { - selectAll(true, false); - songs = getSelectedSongs(); - - // Also delete all directories - for(Entry album: albums) { - deleteRecursively(album); + for(Entry entry: entries) { + if(entry.isDirectory()) { + deleteRecursively(entry); + } else { + songs.add(entry); + } } } if (getDownloadService() != null) { @@ -1067,11 +967,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter @Override protected void done(Void result) { - for(int i = indexes.size() - 1; i >= 0; i--) { - entryList.setItemChecked(indexes.get(i) + 1, false); - entryAdapter.removeAt(indexes.get(i)); + for(Integer index: indexes) { + entryGridAdapter.removeAt(index); } - entryAdapter.notifyDataSetChanged(); Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name)); } @@ -1156,8 +1054,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter @Override protected void done(Void result) { - entries.remove(episode); - entryAdapter.notifyDataSetChanged(); + entryGridAdapter.removeEntry(episode); } @Override @@ -1218,10 +1115,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter Util.toast(context, context.getResources().getString(R.string.starring_content_unstarred, Integer.toString(unstar.size()))); for(Entry entry: unstar) { - entries.remove(entry); + entryGridAdapter.removeEntry(entry); } - entryAdapter.notifyDataSetChanged(); - selectAll(false, false); } @Override @@ -1340,25 +1235,13 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } private View createHeader() { - View header = entryList.findViewById(R.id.select_album_header_wrapper); - boolean add = false; - if(header == null) { - header = LayoutInflater.from(context).inflate(R.layout.select_album_header, entryList, false); - add = true; - } + View header = LayoutInflater.from(context).inflate(R.layout.select_album_header, null, false); setupCoverArt(header); setupTextDisplay(header); + setupButtonEvents(header); - if(add) { - setupButtonEvents(header); - } - - if(add) { - return header; - } else { - return null; - } + return header; } private void setupCoverArt(View header) { @@ -1507,11 +1390,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } else { artistView.setMaxLines(minLines); } - - if(albumList instanceof HeaderGridView) { - HeaderGridView headerGridView = (HeaderGridView) albumList; - ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged(); - } } }); artistView.setMovementMethod(LinkMovementMethod.getInstance()); diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java index fcae3a5c..6070185d 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java @@ -34,6 +34,7 @@ import android.os.Bundle; import android.os.StatFs; import android.support.v4.app.Fragment; import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.ContextMenu; import android.view.GestureDetector; @@ -76,7 +77,6 @@ import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.LoadingTask; import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; -import github.daneren2005.dsub.view.AlbumCell; import github.daneren2005.dsub.view.AlbumView; import github.daneren2005.dsub.view.ArtistEntryView; import github.daneren2005.dsub.view.ArtistView; @@ -308,14 +308,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } } // Apply similar logic to album views - else if(info.targetView instanceof AlbumCell || info.targetView instanceof AlbumView - || info.targetView instanceof ArtistView || info.targetView instanceof ArtistEntryView) { + else if(info.targetView instanceof AlbumView || info.targetView instanceof ArtistView || info.targetView instanceof ArtistEntryView) { File folder = null; int id = 0; - if(info.targetView instanceof AlbumCell) { - folder = ((AlbumCell) info.targetView).getFile(); - id = R.id.album_menu_delete; - } else if(info.targetView instanceof AlbumView) { + if(info.targetView instanceof AlbumView) { folder = ((AlbumView) info.targetView).getFile(); id = R.id.album_menu_delete; } else if(info.targetView instanceof ArtistView) { @@ -632,6 +628,29 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR R.color.holo_red_light); } } + protected void setupScrollList(final RecyclerView recyclerView) { + if(!context.isTouchscreen()) { + refreshLayout.setEnabled(false); + } else { + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + refreshLayout.setEnabled(!recyclerView.canScrollVertically(-1)); + } + }); + + refreshLayout.setColorScheme( + R.color.holo_blue_light, + R.color.holo_orange_light, + R.color.holo_green_light, + R.color.holo_red_light); + } + } protected void warnIfStorageUnavailable() { if (!Util.isExternalStoragePresent()) { @@ -1788,11 +1807,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR return; } - for (Entry song : parent.getChildren(false, true)) { - if (!song.isVideo() && song.getRating() != 1) { - songs.add(song); - } - } for (Entry dir : parent.getChildren(true, false)) { if(dir.getRating() == 1) { continue; @@ -1806,6 +1820,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } getSongsRecursively(musicDirectory, songs); } + + for (Entry song : parent.getChildren(false, true)) { + if (!song.isVideo() && song.getRating() != 1) { + songs.add(song); + } + } } @Override diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java deleted file mode 100644 index 8707ece7..00000000 --- a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - This file is part of Subsonic. - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - Copyright 2014 (C) Scott Jackson -*/ - -package github.daneren2005.dsub.view; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.RatingBar; -import android.widget.TextView; - -import java.io.File; - -import github.daneren2005.dsub.R; -import github.daneren2005.dsub.domain.MusicDirectory; -import github.daneren2005.dsub.util.FileUtil; -import github.daneren2005.dsub.util.ImageLoader; - -public class AlbumCell extends UpdateView { - private static final String TAG = AlbumCell.class.getSimpleName(); - - private Context context; - private MusicDirectory.Entry album; - private File file; - - private View coverArtView; - private TextView titleView; - private TextView artistView; - private boolean showArtist = true; - - public AlbumCell(Context context) { - super(context); - this.context = context; - LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true); - - coverArtView = findViewById(R.id.album_coverart); - titleView = (TextView) findViewById(R.id.album_title); - artistView = (TextView) findViewById(R.id.album_artist); - - ratingBar = (RatingBar) findViewById(R.id.album_rating); - ratingBar.setFocusable(false); - starButton = (ImageButton) findViewById(R.id.album_star); - starButton.setFocusable(false); - moreButton = (ImageView) findViewById(R.id.album_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); - } - - public void setShowArtist(boolean showArtist) { - this.showArtist = showArtist; - } - - protected void setObjectImpl(Object obj1, Object obj2) { - this.album = (MusicDirectory.Entry) obj1; - titleView.setText(album.getAlbumDisplay()); - String artist = ""; - if(showArtist) { - artist = album.getArtist(); - if (artist == null) { - artist = ""; - } - if (album.getYear() != null) { - artist += " - " + album.getYear(); - } - } else if(album.getYear() != null) { - artist += album.getYear(); - } - artistView.setText(album.getArtist() == null ? "" : artist); - imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true); - file = null; - } - - @Override - protected void updateBackground() { - if(file == null) { - file = FileUtil.getAlbumDirectory(context, album); - } - - exists = file.exists(); - isStarred = album.isStarred(); - isRated = album.getRating(); - } - - public MusicDirectory.Entry getEntry() { - return album; - } - - public File getFile() { - return file; - } -} diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java index bd54ea1e..2eb30037 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java @@ -16,6 +16,7 @@ Copyright 2009 (C) Sindre Mehus */ + package github.daneren2005.dsub.view; import android.content.Context; @@ -34,11 +35,6 @@ import github.daneren2005.dsub.util.Util; import java.io.File; import java.util.List; -/** - * Used to display albums in a {@code ListView}. - * - * @author Sindre Mehus - */ public class AlbumView extends UpdateView { private static final String TAG = AlbumView.class.getSimpleName(); @@ -46,42 +42,55 @@ public class AlbumView extends UpdateView { private MusicDirectory.Entry album; private File file; + private View coverArtView; private TextView titleView; private TextView artistView; - private View coverArtView; + private boolean showArtist = true; - public AlbumView(Context context) { + public AlbumView(Context context, boolean cell) { super(context); this.context = context; - LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); + if(cell) { + LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true); + } else { + LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); + } + + coverArtView = findViewById(R.id.album_coverart); titleView = (TextView) findViewById(R.id.album_title); artistView = (TextView) findViewById(R.id.album_artist); - coverArtView = findViewById(R.id.album_coverart); + ratingBar = (RatingBar) findViewById(R.id.album_rating); + ratingBar.setFocusable(false); starButton = (ImageButton) findViewById(R.id.album_star); starButton.setFocusable(false); + moreButton = (ImageView) findViewById(R.id.more_button); - moreButton = (ImageView) findViewById(R.id.album_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); + setClickable(true); + setLongClickable(true); + } + + public void setShowArtist(boolean showArtist) { + this.showArtist = showArtist; } protected void setObjectImpl(Object obj1, Object obj2) { this.album = (MusicDirectory.Entry) obj1; titleView.setText(album.getAlbumDisplay()); - String artist = album.getArtist(); - if(artist == null) { - artist = ""; - } - if(album.getYear() != null) { - artist += " - " + album.getYear(); + String artist = ""; + if(showArtist) { + artist = album.getArtist(); + if (artist == null) { + artist = ""; + } + if (album.getYear() != null) { + artist += " - " + album.getYear(); + } + } else if(album.getYear() != null) { + artist += album.getYear(); } - artistView.setText(artist); - artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE); + artistView.setText(album.getArtist() == null ? "" : artist); imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true); file = null; } diff --git a/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java new file mode 100644 index 00000000..3bb3e8a1 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java @@ -0,0 +1,99 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.graphics.Rect; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.View; + +public class GridSpacingDecoration extends RecyclerView.ItemDecoration { + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + + int spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, view.getResources().getDisplayMetrics()); + int halfSpacing = spacing / 2; + + int childCount = parent.getChildCount(); + int childIndex = parent.getChildPosition(view); + int spanCount = getTotalSpan(view, parent); + int spanIndex = childIndex % spanCount; + int spanSize = getSpanSize(parent, childIndex); + + /* INVALID SPAN */ + if (spanCount < 1 || spanSize > 1) return; + + outRect.top = halfSpacing; + outRect.bottom = halfSpacing; + outRect.left = halfSpacing; + outRect.right = halfSpacing; + + if (isTopEdge(childIndex, spanCount)) { + outRect.top = spacing; + } + + if (isLeftEdge(spanIndex, spanCount)) { + outRect.left = spacing; + } + + if (isRightEdge(spanIndex, spanCount)) { + outRect.right = spacing; + } + + if (isBottomEdge(childIndex, childCount, spanCount)) { + outRect.bottom = spacing; + } + } + + protected int getTotalSpan(View view, RecyclerView parent) { + RecyclerView.LayoutManager mgr = parent.getLayoutManager(); + if (mgr instanceof GridLayoutManager) { + return ((GridLayoutManager) mgr).getSpanCount(); + } + + return -1; + } + protected int getSpanSize(RecyclerView parent, int childIndex) { + RecyclerView.LayoutManager mgr = parent.getLayoutManager(); + if (mgr instanceof GridLayoutManager) { + GridLayoutManager.SpanSizeLookup lookup = ((GridLayoutManager) mgr).getSpanSizeLookup(); + if(lookup != null) { + return lookup.getSpanSize(childIndex); + } + } + + return 1; + } + + protected boolean isLeftEdge(int spanIndex, int spanCount) { + return spanIndex == 0; + } + + protected boolean isRightEdge(int spanIndex, int spanCount) { + return spanIndex == spanCount - 1; + } + + protected boolean isTopEdge(int childIndex, int spanCount) { + return childIndex < spanCount; + } + + protected boolean isBottomEdge(int childIndex, int childCount, int spanCount) { + return childIndex >= childCount - spanCount; + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/SongView.java b/app/src/main/java/github/daneren2005/dsub/view/SongView.java index 2fbaedc3..a5522719 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/SongView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java @@ -83,18 +83,13 @@ public class SongView extends UpdateView implements Checkable { starButton.setFocusable(false); bookmarkButton = (ImageButton) findViewById(R.id.song_bookmark); bookmarkButton.setFocusable(false); - moreButton = (ImageView) findViewById(R.id.artist_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); + moreButton = (ImageView) findViewById(R.id.more_button); bottomRowView = findViewById(R.id.song_bottom); } public void setObjectImpl(Object obj1, Object obj2) { this.song = (MusicDirectory.Entry) obj1; - boolean checkable = (Boolean) obj2; + checkable = (Boolean) obj2; StringBuilder artist = new StringBuilder(40); diff --git a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java b/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java deleted file mode 100644 index 3047d5d7..00000000 --- a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java +++ /dev/null @@ -1,128 +0,0 @@ -package github.daneren2005.dsub.view; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.widget.AbsListView; -import android.widget.GridView; -import android.widget.ListAdapter; - -import java.lang.reflect.Field; - -/** - * Created by Scott on 4/26/2014. - */ -public class UnscrollableGridView extends GridView { - private static final String TAG = UnscrollableGridView.class.getSimpleName(); - - public UnscrollableGridView(Context context) { - super(context); - } - - public UnscrollableGridView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public UnscrollableGridView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public int getColumnWidth() { - // This method will be called from onMeasure() too. - // It's better to use getMeasuredWidth(), as it is safe in this case. - - int hSpacing = 20; - try { - Field field = GridView.class.getDeclaredField("mHorizontalSpacing"); - field.setAccessible(true); - hSpacing = field.getInt(this); - } catch(Exception e) { - - } - - final int totalHorizontalSpacing = getNumColumnsCompat() > 0 ? (getNumColumnsCompat() - 1) * hSpacing : 0; - return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalHorizontalSpacing) / getNumColumnsCompat(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Sets the padding for this view. - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - final int measuredWidth = getMeasuredWidth(); - final int childWidth = getColumnWidth(); - int childHeight = 0; - - // If there's an adapter, use it to calculate the height of this view. - final ListAdapter adapter = getAdapter(); - final int count; - - // There shouldn't be any inherent size (due to padding) if there are no child views. - if (adapter == null || (count = adapter.getCount()) == 0) { - setMeasuredDimension(0, 0); - return; - } - - // Get the first child from the adapter. - final View child = adapter.getView(0, null, this); - if (child != null) { - // Set a default LayoutParams on the child, if it doesn't have one on its own. - AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams(); - if (params == null) { - params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT, - AbsListView.LayoutParams.WRAP_CONTENT); - child.setLayoutParams(params); - } - - // Measure the exact width of the child, and the height based on the width. - // Note: the child takes care of calculating its height. - int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY); - int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - child.measure(childWidthSpec, childHeightSpec); - childHeight = child.getMeasuredHeight(); - } - - int vSpacing = 10; - try { - Field field = GridView.class.getDeclaredField("mVerticalSpacing"); - field.setAccessible(true); - vSpacing = field.getInt(this); - } catch(Exception e) { - - } - - // Number of rows required to 'mTotal' items. - final int rows = (int) Math.ceil((double) getCount() / getNumColumnsCompat()); - final int childrenHeight = childHeight * rows; - final int totalVerticalSpacing = rows > 0 ? (rows - 1) * vSpacing : 0; - - // Total height of this view. - final int measuredHeight = Math.abs(childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing); - setMeasuredDimension(measuredWidth, measuredHeight); - } - - private int getNumColumnsCompat() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - return getNumColumnsCompat11(); - } else { - int columns = 0; - int children = getChildCount(); - if (children > 0) { - int width = getChildAt(0).getMeasuredWidth(); - if (width > 0) { - columns = getWidth() / width; - } - } - return columns > 0 ? columns : AUTO_FIT; - } - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private int getNumColumnsCompat11() { - return getNumColumns(); - } -} diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java index f9c62121..bdcdc46f 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java @@ -23,6 +23,7 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.os.Handler; import android.os.Looper; +import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -65,6 +66,8 @@ public class UpdateView extends LinearLayout { protected SilentBackgroundTask imageTask = null; protected final boolean autoUpdate; + protected int position; + protected boolean checkable; public UpdateView(Context context) { this(context, true); @@ -224,8 +227,6 @@ public class UpdateView extends LinearLayout { MusicDirectory.Entry check = null; if(view instanceof SongView) { check = ((SongView) view).getEntry(); - } else if(view instanceof AlbumCell) { - check = ((AlbumCell) view).getEntry(); } else if(view instanceof AlbumView) { check = ((AlbumView) view).getEntry(); } @@ -283,4 +284,34 @@ public class UpdateView extends LinearLayout { rating = isRated; } } + + public void setPosition(int position) { + this.position = position; + } + public int getPosition() { + return position; + } + + public boolean isCheckable() { + return checkable; + } + + public static class UpdateViewHolder extends RecyclerView.ViewHolder { + private UpdateView updateView; + + public UpdateViewHolder(UpdateView itemView) { + super(itemView); + + this.updateView = itemView; + } + + // Different is so that call is not ambiguous + public UpdateViewHolder(View view, boolean different) { + super(view); + } + + public UpdateView getUpdateView() { + return updateView; + } + } } diff --git a/app/src/main/res/layout/album_cell_item.xml b/app/src/main/res/layout/album_cell_item.xml index 3f708e63..46e2b1a6 100644 --- a/app/src/main/res/layout/album_cell_item.xml +++ b/app/src/main/res/layout/album_cell_item.xml @@ -2,7 +2,8 @@ + android:layout_height="match_parent" + android:background="@drawable/abc_item_background_holo_light"> - \ No newline at end of file diff --git a/app/src/main/res/layout/select_album.xml b/app/src/main/res/layout/select_album.xml index bbdf0e54..cacbc560 100644 --- a/app/src/main/res/layout/select_album.xml +++ b/app/src/main/res/layout/select_album.xml @@ -17,12 +17,11 @@ - + android:scrollbars="vertical"/> \ No newline at end of file diff --git a/app/src/main/res/layout/song_list_item.xml b/app/src/main/res/layout/song_list_item.xml index 86f77869..26c89b2b 100644 --- a/app/src/main/res/layout/song_list_item.xml +++ b/app/src/main/res/layout/song_list_item.xml @@ -117,7 +117,7 @@ - \ No newline at end of file -- cgit v1.2.3 From 0397dfed318eeb51f5f0788a91195857d3a6501c Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 19 May 2015 08:58:40 -0700 Subject: Update to use isAlbum --- .../java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java index 0a64e8fd..7ab419cf 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -422,7 +422,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr if ("newest".equals(albumListType)) { args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true); } - if(entry.getArtist() == null && entry.getParent() == null) { + if(!entry.isAlbum()) { args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true); } fragment.setArguments(args); -- cgit v1.2.3 From 1bff0c33f4908c6e371ea358dd55b1cef572baae Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 22 May 2015 16:42:57 -0700 Subject: Add loading indicator for infinite loading --- .../dsub/adapter/EntryInfiniteGridAdapter.java | 57 ++++++++++++++++++++-- .../dsub/fragments/SelectDirectoryFragment.java | 2 +- 2 files changed, 55 insertions(+), 4 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java index 3bc3f6aa..288c38a6 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java @@ -16,9 +16,13 @@ package github.daneren2005.dsub.adapter; import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import java.util.List; +import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.MusicDirectory.Entry; import github.daneren2005.dsub.domain.ServerInfo; @@ -26,8 +30,11 @@ import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; import github.daneren2005.dsub.util.ImageLoader; import github.daneren2005.dsub.util.SilentBackgroundTask; +import github.daneren2005.dsub.view.UpdateView; public class EntryInfiniteGridAdapter extends EntryGridAdapter { + public static int VIEW_TYPE_LOADING = 4; + private String type; private String extra; private int size; @@ -39,6 +46,44 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter { super(context, entries, imageLoader, largeCell); } + @Override + public UpdateView.UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if(viewType == VIEW_TYPE_LOADING) { + View progress = LayoutInflater.from(context).inflate(R.layout.tab_progress, null); + progress.setVisibility(View.VISIBLE); + return new UpdateView.UpdateViewHolder(progress, false); + } + + return super.onCreateViewHolder(parent, viewType); + } + + @Override + public int getItemViewType(int position) { + if(isLoadingView(position)) { + return VIEW_TYPE_LOADING; + } + + return super.getItemViewType(position); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, int position) { + if(!isLoadingView(position)) { + super.onBindViewHolder(holder, position); + } + } + + @Override + public int getItemCount() { + int size = super.getItemCount(); + + if(!allLoaded) { + size++; + } + + return size; + } + public void setData(String type, String extra, int size) { this.type = type; this.extra = extra; @@ -57,9 +102,6 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter { @Override protected Void doInBackground() throws Throwable { newData = cacheInBackground(); - if(newData.isEmpty()) { - allLoaded = true; - } return null; } @@ -67,6 +109,11 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter { protected void done(Void result) { appendCachedData(newData); loading = false; + + if(newData.isEmpty()) { + allLoaded = true; + notifyDataSetChanged(); + } } }.execute(); } @@ -92,4 +139,8 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter { this.notifyItemRangeInserted(start, newData.size()); } } + + protected boolean isLoadingView(int position) { + return !allLoaded && position >= entries.size(); + } } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java index 7ab419cf..d427a26d 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -182,7 +182,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr @Override public int getSpanSize(int position) { int viewType = entryGridAdapter.getItemViewType(position); - if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER) { + if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == EntryInfiniteGridAdapter.VIEW_TYPE_LOADING) { return columns; } else { return 1; -- cgit v1.2.3 From f701c7d9ad5a4bd691de7dc3cf47b1967b60b516 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 22 May 2015 17:03:39 -0700 Subject: Fix item selection animation --- .../daneren2005/dsub/adapter/EntryGridAdapter.java | 9 +- .../github/daneren2005/dsub/view/AlbumView.java | 3 - .../daneren2005/dsub/view/HeaderGridView.java | 836 --------------------- app/src/main/res/layout/song_list_item.xml | 3 +- 4 files changed, 7 insertions(+), 844 deletions(-) delete mode 100644 app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java index 6de7ce33..80d1ade9 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java @@ -17,6 +17,7 @@ package github.daneren2005.dsub.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -87,13 +88,13 @@ public class EntryGridAdapter extends RecyclerView.Adapter { if(viewType != VIEW_TYPE_HEADER && updateView != null) { final UpdateView view = updateView; - updateView.setOnClickListener(new View.OnClickListener() { + updateView.getChildAt(0).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Entry entry = getEntryForView(view); - if (view.isCheckable() && v instanceof SongView) { - SongView songView = (SongView) v; + if (view.isCheckable() && view instanceof SongView) { + SongView songView = (SongView) view; if (selected.contains(entry)) { selected.remove(entry); @@ -107,7 +108,7 @@ public class EntryGridAdapter extends RecyclerView.Adapter { } } }); - updateView.setOnLongClickListener(new View.OnLongClickListener() { + updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { Entry entry = getEntryForView(view); diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java index 2eb30037..69f2ef13 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java @@ -66,9 +66,6 @@ public class AlbumView extends UpdateView { starButton = (ImageButton) findViewById(R.id.album_star); starButton.setFocusable(false); moreButton = (ImageView) findViewById(R.id.more_button); - - setClickable(true); - setLongClickable(true); } public void setShowArtist(boolean showArtist) { diff --git a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java b/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java deleted file mode 100644 index 8a82f353..00000000 --- a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java +++ /dev/null @@ -1,836 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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 github.daneren2005.dsub.view; - -import android.annotation.TargetApi; -import android.content.Context; -import android.database.DataSetObservable; -import android.database.DataSetObserver; -import android.os.Build; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.*; - -import java.lang.reflect.Field; -import java.util.ArrayList; - -/** - * A {@link GridView} that supports adding header rows in a - * very similar way to {@link android.widget.ListView}. - * See {@link HeaderGridView#addHeaderView(View, Object, boolean)} - * See {@link HeaderGridView#addFooterView(View, Object, boolean)} - */ -public class HeaderGridView extends GridView { - private static final String TAG = HeaderGridView.class.getSimpleName(); - public static boolean DEBUG = false; - - /** - * A class that represents a fixed view in a list, for example a header at the top - * or a footer at the bottom. - */ - private static class FixedViewInfo { - /** - * The view to add to the grid - */ - public View view; - public ViewGroup viewContainer; - /** - * The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. - */ - public Object data; - /** - * true if the fixed view should be selectable in the grid - */ - public boolean isSelectable; - } - - private int mNumColumns = AUTO_FIT; - private View mViewForMeasureRowHeight = null; - private int mRowHeight = -1; - private static final String LOG_TAG = HeaderGridView.class.getSimpleName(); - - private ArrayList mHeaderViewInfos = new ArrayList(); - private ArrayList mFooterViewInfos = new ArrayList(); - - private void initHeaderGridView() { - } - - public HeaderGridView(Context context) { - super(context); - initHeaderGridView(); - } - - public HeaderGridView(Context context, AttributeSet attrs) { - super(context, attrs); - initHeaderGridView(); - } - - public HeaderGridView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initHeaderGridView(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - ListAdapter adapter = getAdapter(); - if (adapter != null && adapter instanceof HeaderViewGridAdapter) { - ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompatible()); - ((HeaderViewGridAdapter) adapter).setRowHeight(getRowHeight()); - } - } - - @Override - public void setClipChildren(boolean clipChildren) { - // Ignore, since the header rows depend on not being clipped - } - - /** - * Do not call this method unless you know how it works. - * - * @param clipChildren - */ - public void setClipChildrenSupper(boolean clipChildren) { - super.setClipChildren(false); - } - - /** - * Add a fixed view to appear at the top of the grid. If addHeaderView is - * called more than once, the views will appear in the order they were - * added. Views added using this call can take focus if they want. - *

- * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap - * the supplied cursor with one that will also account for header views. - * - * @param v The view to add. - */ - public void addHeaderView(View v) { - addHeaderView(v, null, true); - } - - /** - * Add a fixed view to appear at the top of the grid. If addHeaderView is - * called more than once, the views will appear in the order they were - * added. Views added using this call can take focus if they want. - *

- * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap - * the supplied cursor with one that will also account for header views. - * - * @param v The view to add. - * @param data Data to associate with this view - * @param isSelectable whether the item is selectable - */ - public void addHeaderView(View v, Object data, boolean isSelectable) { - ListAdapter adapter = getAdapter(); - if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) { - throw new IllegalStateException( - "Cannot add header view to grid -- setAdapter has already been called."); - } - - ViewGroup.LayoutParams lyp = v.getLayoutParams(); - - FixedViewInfo info = new FixedViewInfo(); - FrameLayout fl = new FullWidthFixedViewLayout(getContext()); - - if (lyp != null) { - v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); - fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); - } - fl.addView(v); - info.view = v; - info.viewContainer = fl; - info.data = data; - info.isSelectable = isSelectable; - mHeaderViewInfos.add(info); - // in the case of re-adding a header view, or adding one later on, - // we need to notify the observer - if (adapter != null) { - ((HeaderViewGridAdapter) adapter).notifyDataSetChanged(); - } - } - - public void addFooterView(View v) { - addFooterView(v, null, true); - } - - public void addFooterView(View v, Object data, boolean isSelectable) { - ListAdapter mAdapter = getAdapter(); - if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) { - throw new IllegalStateException( - "Cannot add header view to grid -- setAdapter has already been called."); - } - - ViewGroup.LayoutParams lyp = v.getLayoutParams(); - - FixedViewInfo info = new FixedViewInfo(); - FrameLayout fl = new FullWidthFixedViewLayout(getContext()); - - if (lyp != null) { - v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); - fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); - } - fl.addView(v); - info.view = v; - info.viewContainer = fl; - info.data = data; - info.isSelectable = isSelectable; - mFooterViewInfos.add(info); - - if (mAdapter != null) { - ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged(); - } - } - - public int getHeaderViewCount() { - return mHeaderViewInfos.size(); - } - - public int getFooterViewCount() { - return mFooterViewInfos.size(); - } - - /** - * Removes a previously-added header view. - * - * @param v The view to remove - * @return true if the view was removed, false if the view was not a header - * view - */ - public boolean removeHeaderView(View v) { - if (mHeaderViewInfos.size() > 0) { - boolean result = false; - ListAdapter adapter = getAdapter(); - if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) { - result = true; - } - removeFixedViewInfo(v, mHeaderViewInfos); - return result; - } - return false; - } - - /** - * Removes a previously-added footer view. - * - * @param v The view to remove - * @return true if the view was removed, false if the view was not a header - * view - */ - public boolean removeFooterView(View v) { - if (mFooterViewInfos.size() > 0) { - boolean result = false; - ListAdapter adapter = getAdapter(); - if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) { - result = true; - } - removeFixedViewInfo(v, mFooterViewInfos); - return result; - } - return false; - } - - private void removeFixedViewInfo(View v, ArrayList where) { - int len = where.size(); - for (int i = 0; i < len; ++i) { - FixedViewInfo info = where.get(i); - if (info.view == v) { - where.remove(i); - break; - } - } - } - - @TargetApi(11) - private int getNumColumnsCompatible() { - if (Build.VERSION.SDK_INT >= 11) { - return super.getNumColumns(); - } else { - try { - Field numColumns = GridView.class.getSuperclass().getDeclaredField("mNumColumns"); - numColumns.setAccessible(true); - return numColumns.getInt(this); - } catch (Exception e) { - if (mNumColumns != -1) { - return mNumColumns; - } else { - return 2; - } - } - } - } - - @TargetApi(16) - private int getColumnWidthCompatible() { - if (Build.VERSION.SDK_INT >= 16) { - return super.getColumnWidth(); - } else { - try { - Field numColumns = getClass().getSuperclass().getDeclaredField("mColumnWidth"); - numColumns.setAccessible(true); - return numColumns.getInt(this); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mViewForMeasureRowHeight = null; - } - - public void invalidateRowHeight() { - mRowHeight = -1; - } - - public int getHeaderHeight(int row) { - if (row >= 0) { - return mHeaderViewInfos.get(row).view.getMeasuredHeight(); - } - - return 0; - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - public int getVerticalSpacing(){ - int value = 0; - - try { - int currentapiVersion = android.os.Build.VERSION.SDK_INT; - if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){ - Field field = this.getClass().getSuperclass().getDeclaredField("mVerticalSpacing"); - field.setAccessible(true); - value = field.getInt(this); - } else{ - value = super.getVerticalSpacing(); - } - - }catch (Exception ex){ - - } - - return value; - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - public int getHorizontalSpacing(){ - int value = 0; - - try { - int currentapiVersion = android.os.Build.VERSION.SDK_INT; - if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){ - Field field = this.getClass().getSuperclass().getDeclaredField("mHorizontalSpacing"); - field.setAccessible(true); - value = field.getInt(this); - } else{ - value = super.getHorizontalSpacing(); - } - - }catch (Exception ex){ - - } - - return value; - } - - public int getRowHeight() { - if (mRowHeight > 0) { - // return mRowHeight; - } - ListAdapter adapter = getAdapter(); - int numColumns = getNumColumnsCompatible(); - - // adapter has not been set or has no views in it; - if (adapter == null || adapter.getCount() <= numColumns * (mHeaderViewInfos.size() + mFooterViewInfos.size()) || numColumns == -1) { - return -1; - } - int mColumnWidth = getColumnWidthCompatible(); - View view = getAdapter().getView(numColumns * mHeaderViewInfos.size(), mViewForMeasureRowHeight, this); - AbsListView.LayoutParams p = (AbsListView.LayoutParams) view.getLayoutParams(); - if (p == null) { - p = new AbsListView.LayoutParams(-1, -2, 0); - view.setLayoutParams(p); - } - int childHeightSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height); - int childWidthSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width); - view.measure(childWidthSpec, childHeightSpec); - mViewForMeasureRowHeight = view; - mRowHeight = view.getMeasuredHeight(); - return mRowHeight; - } - - @TargetApi(11) - public void tryToScrollToBottomSmoothly() { - int lastPos = getAdapter().getCount() - 1; - if (Build.VERSION.SDK_INT >= 11) { - smoothScrollToPositionFromTop(lastPos, 0); - } else { - setSelection(lastPos); - } - } - - @TargetApi(11) - public void tryToScrollToBottomSmoothly(int duration) { - int lastPos = getAdapter().getCount() - 1; - if (Build.VERSION.SDK_INT >= 11) { - smoothScrollToPositionFromTop(lastPos, 0, duration); - } else { - setSelection(lastPos); - } - } - - @Override - public void setAdapter(ListAdapter adapter) { - if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) { - HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); - int numColumns = getNumColumnsCompatible(); - if (numColumns > 1) { - headerViewGridAdapter.setNumColumns(numColumns); - } - headerViewGridAdapter.setRowHeight(getRowHeight()); - super.setAdapter(headerViewGridAdapter); - } else { - super.setAdapter(adapter); - } - } - - /** - * full width - */ - private class FullWidthFixedViewLayout extends FrameLayout { - - public FullWidthFixedViewLayout(Context context) { - super(context); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int realLeft = HeaderGridView.this.getPaddingLeft() + getPaddingLeft(); - // Try to make where it should be, from left, full width - if (realLeft != left) { - offsetLeftAndRight(realLeft - left); - } - super.onLayout(changed, left, top, right, bottom); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int targetWidth = HeaderGridView.this.getMeasuredWidth() - - HeaderGridView.this.getPaddingLeft() - - HeaderGridView.this.getPaddingRight(); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, - MeasureSpec.getMode(widthMeasureSpec)); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - @Override - public void setNumColumns(int numColumns) { - super.setNumColumns(numColumns); - mNumColumns = numColumns; - ListAdapter adapter = getAdapter(); - if (adapter != null && adapter instanceof HeaderViewGridAdapter) { - ((HeaderViewGridAdapter) adapter).setNumColumns(numColumns); - } - } - - /** - * ListAdapter used when a HeaderGridView has header views. This ListAdapter - * wraps another one and also keeps track of the header views and their - * associated data objects. - *

This is intended as a base class; you will probably not need to - * use this class directly in your own code. - */ - private static class HeaderViewGridAdapter extends BaseAdapter implements WrapperListAdapter, Filterable { - // This is used to notify the container of updates relating to number of columns - // or headers changing, which changes the number of placeholders needed - private final DataSetObservable mDataSetObservable = new DataSetObservable(); - private final ListAdapter mAdapter; - static final ArrayList EMPTY_INFO_LIST = - new ArrayList(); - - // This ArrayList is assumed to NOT be null. - ArrayList mHeaderViewInfos; - ArrayList mFooterViewInfos; - private int mNumColumns = 1; - private int mRowHeight = -1; - boolean mAreAllFixedViewsSelectable; - private final boolean mIsFilterable; - private boolean mCachePlaceHoldView = true; - // From Recycle Bin or calling getView, this a question... - private boolean mCacheFirstHeaderView = false; - - public HeaderViewGridAdapter(ArrayList headerViewInfos, ArrayList footViewInfos, ListAdapter adapter) { - mAdapter = adapter; - mIsFilterable = adapter instanceof Filterable; - if (headerViewInfos == null) { - mHeaderViewInfos = EMPTY_INFO_LIST; - } else { - mHeaderViewInfos = headerViewInfos; - } - - if (footViewInfos == null) { - mFooterViewInfos = EMPTY_INFO_LIST; - } else { - mFooterViewInfos = footViewInfos; - } - mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) - && areAllListInfosSelectable(mFooterViewInfos); - } - - public void setNumColumns(int numColumns) { - if (numColumns < 1) { - return; - } - if (mNumColumns != numColumns) { - mNumColumns = numColumns; - notifyDataSetChanged(); - } - } - - public void setRowHeight(int height) { - mRowHeight = height; - } - - public int getHeadersCount() { - return mHeaderViewInfos.size(); - } - - public int getFootersCount() { - return mFooterViewInfos.size(); - } - - @Override - public boolean isEmpty() { - return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0; - } - - private boolean areAllListInfosSelectable(ArrayList infos) { - if (infos != null) { - for (FixedViewInfo info : infos) { - if (!info.isSelectable) { - return false; - } - } - } - return true; - } - - public boolean removeHeader(View v) { - for (int i = 0; i < mHeaderViewInfos.size(); i++) { - FixedViewInfo info = mHeaderViewInfos.get(i); - if (info.view == v) { - mHeaderViewInfos.remove(i); - mAreAllFixedViewsSelectable = - areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); - mDataSetObservable.notifyChanged(); - return true; - } - } - return false; - } - - public boolean removeFooter(View v) { - for (int i = 0; i < mFooterViewInfos.size(); i++) { - FixedViewInfo info = mFooterViewInfos.get(i); - if (info.view == v) { - mFooterViewInfos.remove(i); - mAreAllFixedViewsSelectable = - areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); - mDataSetObservable.notifyChanged(); - return true; - } - } - return false; - } - - @Override - public int getCount() { - if (mAdapter != null) { - return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount(); - } else { - return (getFootersCount() + getHeadersCount()) * mNumColumns; - } - } - - @Override - public boolean areAllItemsEnabled() { - if (mAdapter != null) { - return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled(); - } else { - return true; - } - } - - private int getAdapterAndPlaceHolderCount() { - final int adapterCount = (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns); - return adapterCount; - } - - @Override - public boolean isEnabled(int position) { - // Header (negative positions will throw an IndexOutOfBoundsException) - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders) { - return position % mNumColumns == 0 - && mHeaderViewInfos.get(position / mNumColumns).isSelectable; - } - - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition < adapterCount) { - return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition); - } - } - - // Footer (off-limits positions will throw an IndexOutOfBoundsException) - final int footerPosition = adjPosition - adapterCount; - return footerPosition % mNumColumns == 0 - && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable; - } - - @Override - public Object getItem(int position) { - // Header (negative positions will throw an ArrayIndexOutOfBoundsException) - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders) { - if (position % mNumColumns == 0) { - return mHeaderViewInfos.get(position / mNumColumns).data; - } - return null; - } - - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition < adapterCount) { - if (adjPosition < mAdapter.getCount()) { - return mAdapter.getItem(adjPosition); - } else { - return null; - } - } - } - - // Footer (off-limits positions will throw an IndexOutOfBoundsException) - final int footerPosition = adjPosition - adapterCount; - if (footerPosition % mNumColumns == 0) { - return mFooterViewInfos.get(footerPosition).data; - } else { - return null; - } - } - - @Override - public long getItemId(int position) { - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (mAdapter != null && position >= numHeadersAndPlaceholders) { - int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = mAdapter.getCount(); - if (adjPosition < adapterCount) { - return mAdapter.getItemId(adjPosition); - } - } - return -1; - } - - @Override - public boolean hasStableIds() { - if (mAdapter != null) { - return mAdapter.hasStableIds(); - } - return false; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (DEBUG) { - Log.d(LOG_TAG, String.format("getView: %s, reused: %s", position, convertView == null)); - } - // Header (negative positions will throw an ArrayIndexOutOfBoundsException) - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders) { - View headerViewContainer = mHeaderViewInfos - .get(position / mNumColumns).viewContainer; - if (position % mNumColumns == 0) { - return headerViewContainer; - } else { - if (convertView == null) { - convertView = new View(parent.getContext()); - } - // We need to do this because GridView uses the height of the last item - // in a row to determine the height for the entire row. - convertView.setVisibility(View.INVISIBLE); - convertView.setMinimumHeight(headerViewContainer.getHeight()); - return convertView; - } - } - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition < adapterCount) { - if (adjPosition < mAdapter.getCount()) { - View view = mAdapter.getView(adjPosition, convertView, parent); - return view; - } else { - if (convertView == null) { - convertView = new View(parent.getContext()); - } - convertView.setVisibility(View.INVISIBLE); - convertView.setMinimumHeight(mRowHeight); - return convertView; - } - } - } - // Footer - final int footerPosition = adjPosition - adapterCount; - if (footerPosition < getCount()) { - View footViewContainer = mFooterViewInfos - .get(footerPosition / mNumColumns).viewContainer; - if (position % mNumColumns == 0) { - return footViewContainer; - } else { - if (convertView == null) { - convertView = new View(parent.getContext()); - } - // We need to do this because GridView uses the height of the last item - // in a row to determine the height for the entire row. - convertView.setVisibility(View.INVISIBLE); - convertView.setMinimumHeight(footViewContainer.getHeight()); - return convertView; - } - } - throw new ArrayIndexOutOfBoundsException(position); - } - - @Override - public int getItemViewType(int position) { - - final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1; - int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; - if (mCachePlaceHoldView) { - // Header - if (position < numHeadersAndPlaceholders) { - if (position == 0) { - if (mCacheFirstHeaderView) { - type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1; - } - } - if (position % mNumColumns != 0) { - type = adapterViewTypeStart + (position / mNumColumns + 1); - } - } - } - - // Adapter - final int adjPosition = position - numHeadersAndPlaceholders; - int adapterCount = 0; - if (mAdapter != null) { - adapterCount = getAdapterAndPlaceHolderCount(); - if (adjPosition >= 0 && adjPosition < adapterCount) { - if (adjPosition < mAdapter.getCount()) { - type = mAdapter.getItemViewType(adjPosition); - } else { - if (mCachePlaceHoldView) { - type = adapterViewTypeStart + mHeaderViewInfos.size() + 1; - } - } - } - } - - if (mCachePlaceHoldView) { - // Footer - final int footerPosition = adjPosition - adapterCount; - if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) { - type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1); - } - } - if (DEBUG) { - Log.d(LOG_TAG, String.format("getItemViewType: pos: %s, result: %s", position, type, mCachePlaceHoldView, mCacheFirstHeaderView)); - } - return type; - } - - /** - * content view, content view holder, header[0], header and footer placeholder(s) - * - * @return - */ - @Override - public int getViewTypeCount() { - int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount(); - if (mCachePlaceHoldView) { - int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size(); - if (mCacheFirstHeaderView) { - offset += 1; - } - count += offset; - } - if (DEBUG) { - Log.d(LOG_TAG, String.format("getViewTypeCount: %s", count)); - } - return count; - } - - @Override - public void registerDataSetObserver(DataSetObserver observer) { - mDataSetObservable.registerObserver(observer); - if (mAdapter != null) { - mAdapter.registerDataSetObserver(observer); - } - } - - @Override - public void unregisterDataSetObserver(DataSetObserver observer) { - mDataSetObservable.unregisterObserver(observer); - if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(observer); - } - } - - @Override - public Filter getFilter() { - if (mIsFilterable) { - return ((Filterable) mAdapter).getFilter(); - } - return null; - } - - @Override - public ListAdapter getWrappedAdapter() { - return mAdapter; - } - - public void notifyDataSetChanged() { - mDataSetObservable.notifyChanged(); - } - } -} diff --git a/app/src/main/res/layout/song_list_item.xml b/app/src/main/res/layout/song_list_item.xml index 26c89b2b..80996510 100644 --- a/app/src/main/res/layout/song_list_item.xml +++ b/app/src/main/res/layout/song_list_item.xml @@ -3,7 +3,8 @@ android:id="@id/drag_handle" android:orientation="horizontal" android:layout_width="fill_parent" - android:layout_height="?android:attr/listPreferredItemHeight"> + android:layout_height="?android:attr/listPreferredItemHeight" + android:background="@drawable/abc_item_background_holo_light"> Date: Fri, 22 May 2015 17:33:20 -0700 Subject: Add abstract SelectRecyclerFragment --- .../dsub/fragments/SelectRecyclerFragment.java | 191 +++++++++++++++++++++ .../main/res/layout/abstract_recycler_fragment.xml | 26 +++ 2 files changed, 217 insertions(+) create mode 100644 app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java create mode 100644 app/src/main/res/layout/abstract_recycler_fragment.xml (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java new file mode 100644 index 00000000..6fd56fd1 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -0,0 +1,191 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ +package github.daneren2005.dsub.fragments; + +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +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.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.service.MusicService; +import github.daneren2005.dsub.service.MusicServiceFactory; +import github.daneren2005.dsub.util.BackgroundTask; +import github.daneren2005.dsub.util.Constants; +import github.daneren2005.dsub.util.ProgressListener; +import github.daneren2005.dsub.util.TabBackgroundTask; +import github.daneren2005.dsub.util.Util; +import github.daneren2005.dsub.view.GridSpacingDecoration; + +public abstract class SelectRecyclerFragment extends SubsonicFragment { + private static final String TAG = SelectRecyclerFragment.class.getSimpleName(); + protected RecyclerView recyclerView; + protected RecyclerView.Adapter adapter; + protected BackgroundTask> currentTask; + protected List objects; + protected boolean serialize = true; + protected boolean largeCells; + protected int columns; + + @Override + public void onCreate(Bundle bundle) { + super.onCreate(bundle); + + if(bundle != null && serialize) { + objects = (List) bundle.getSerializable(Constants.FRAGMENT_LIST); + } + largeCells = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true); + columns = context.getResources().getInteger(R.integer.Grid_Columns); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if(serialize) { + outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) objects); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { + rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false); + + refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout); + refreshLayout.setOnRefreshListener(this); + + recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler); + setupLayoutManager(); + setupScrollList(recyclerView); + + if(objects == null) { + refresh(false); + } else { + recyclerView.setAdapter(adapter = getAdapter(objects)); + } + + return rootView; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { + if(!primaryFragment) { + return; + } + + menuInflater.inflate(getOptionsMenu(), menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + protected void refresh(final boolean refresh) { + int titleRes = getTitleResource(); + if(titleRes != 0) { + setTitle(getTitleResource()); + } + recyclerView.setVisibility(View.GONE); + + // Cancel current running task before starting another one + if(currentTask != null) { + currentTask.cancel(); + } + + currentTask = new TabBackgroundTask>(this) { + @Override + protected List doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(context); + + objects = new ArrayList(); + + try { + objects = getObjects(musicService, refresh, this); + } catch (Exception x) { + Log.e(TAG, "Failed to load", x); + } + + return objects; + } + + @Override + protected void done(List result) { + if (result != null && !result.isEmpty()) { + recyclerView.setAdapter(adapter = getAdapter(result)); + + onFinishRefresh(); + recyclerView.setVisibility(View.VISIBLE); + } else { + setEmpty(true); + } + + currentTask = null; + } + }; + currentTask.execute(); + } + + public void setupLayoutManager() { + if(largeCells) { + final int columns = context.getResources().getInteger(R.integer.Grid_Columns); + GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns); + + GridLayoutManager.SpanSizeLookup spanSizeLookup = getSpanSizeLookup(); + if(spanSizeLookup != null) { + gridLayoutManager.setSpanSizeLookup(spanSizeLookup); + } + RecyclerView.ItemDecoration itemDecoration = getItemDecoration(); + if(itemDecoration != null) { + recyclerView.addItemDecoration(itemDecoration); + } + recyclerView.setLayoutManager(gridLayoutManager); + } else { + LinearLayoutManager layoutManager = new LinearLayoutManager(context); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + recyclerView.setLayoutManager(layoutManager); + } + } + public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() { + return null; + } + public RecyclerView.ItemDecoration getItemDecoration() { + return new GridSpacingDecoration(); + } + + public abstract int getOptionsMenu(); + public abstract RecyclerView.Adapter getAdapter(List objs); + public abstract List getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception; + public abstract int getTitleResource(); + + public void onFinishRefresh() { + + } +} diff --git a/app/src/main/res/layout/abstract_recycler_fragment.xml b/app/src/main/res/layout/abstract_recycler_fragment.xml new file mode 100644 index 00000000..26c4971c --- /dev/null +++ b/app/src/main/res/layout/abstract_recycler_fragment.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3 From 096f97542c1f8b9baa6cc53fb2f6909f969e261f Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 22 May 2015 17:56:12 -0700 Subject: Allow more fine tuned control over LayoutManager --- .../dsub/fragments/SelectRecyclerFragment.java | 41 +++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java index 6fd56fd1..c19e9b28 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -153,26 +153,35 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment { currentTask.execute(); } - public void setupLayoutManager() { + private void setupLayoutManager() { + recyclerView.setLayoutManager(getLayoutManager()); + } + public RecyclerView.LayoutManager getLayoutManager() { if(largeCells) { - final int columns = context.getResources().getInteger(R.integer.Grid_Columns); - GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns); - - GridLayoutManager.SpanSizeLookup spanSizeLookup = getSpanSizeLookup(); - if(spanSizeLookup != null) { - gridLayoutManager.setSpanSizeLookup(spanSizeLookup); - } - RecyclerView.ItemDecoration itemDecoration = getItemDecoration(); - if(itemDecoration != null) { - recyclerView.addItemDecoration(itemDecoration); - } - recyclerView.setLayoutManager(gridLayoutManager); + return getGridLayoutManager(); } else { - LinearLayoutManager layoutManager = new LinearLayoutManager(context); - layoutManager.setOrientation(LinearLayoutManager.VERTICAL); - recyclerView.setLayoutManager(layoutManager); + return getLinearLayoutManager(); } } + public GridLayoutManager getGridLayoutManager() { + final int columns = context.getResources().getInteger(R.integer.Grid_Columns); + GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns); + + GridLayoutManager.SpanSizeLookup spanSizeLookup = getSpanSizeLookup(); + if(spanSizeLookup != null) { + gridLayoutManager.setSpanSizeLookup(spanSizeLookup); + } + RecyclerView.ItemDecoration itemDecoration = getItemDecoration(); + if(itemDecoration != null) { + recyclerView.addItemDecoration(itemDecoration); + } + return gridLayoutManager; + } + public LinearLayoutManager getLinearLayoutManager() { + LinearLayoutManager layoutManager = new LinearLayoutManager(context); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + return layoutManager; + } public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() { return null; } -- cgit v1.2.3 From 117c246d7e619ab9a3c0fb36fb152b8ad8bf9afe Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 26 May 2015 18:03:47 -0700 Subject: Abstract out common logic into SectionAdapter --- .../daneren2005/dsub/adapter/EntryGridAdapter.java | 154 ++---------- .../dsub/adapter/EntryInfiniteGridAdapter.java | 8 +- .../daneren2005/dsub/adapter/SectionAdapter.java | 280 +++++++++++++++++++++ .../dsub/fragments/SelectDirectoryFragment.java | 15 +- .../dsub/fragments/SelectRecyclerFragment.java | 13 +- .../daneren2005/dsub/view/BasicHeaderView.java | 37 +++ .../github/daneren2005/dsub/view/UpdateView.java | 21 +- app/src/main/res/layout/basic_header.xml | 11 + app/src/main/res/layout/basic_list_item.xml | 2 +- app/src/main/res/values/strings.xml | 4 +- 10 files changed, 375 insertions(+), 170 deletions(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java create mode 100644 app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java create mode 100644 app/src/main/res/layout/basic_header.xml (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java index 80d1ade9..1a4d96a4 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java @@ -16,7 +16,6 @@ package github.daneren2005.dsub.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -24,7 +23,6 @@ import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; -import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.MusicDirectory.Entry; import github.daneren2005.dsub.util.ImageLoader; @@ -33,30 +31,21 @@ import github.daneren2005.dsub.view.SongView; import github.daneren2005.dsub.view.UpdateView; import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder; -public class EntryGridAdapter extends RecyclerView.Adapter { +public class EntryGridAdapter extends SectionAdapter { private static String TAG = EntryGridAdapter.class.getSimpleName(); - public static int VIEW_TYPE_HEADER = 0; public static int VIEW_TYPE_ALBUM_CELL = 1; public static int VIEW_TYPE_ALBUM_LINE = 2; public static int VIEW_TYPE_SONG = 3; - protected Context context; - protected List entries; private ImageLoader imageLoader; private boolean largeAlbums; private boolean showArtist = false; private boolean checkable = true; - private OnEntryClickedListener onEntryClickedListener; - private View header; - private List selected = new ArrayList(); - private UpdateView contextView; - private Entry contextEntry; public EntryGridAdapter(Context context, List entries, ImageLoader imageLoader, boolean largeCell) { - this.context = context; - this.entries = entries; + super(context, entries); this.imageLoader = imageLoader; this.largeAlbums = largeCell; @@ -74,11 +63,7 @@ public class EntryGridAdapter extends RecyclerView.Adapter { } @Override - public UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if(viewType == VIEW_TYPE_HEADER) { - return new UpdateViewHolder(header, false); - } - + public UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { UpdateView updateView = null; if(viewType == VIEW_TYPE_ALBUM_LINE || viewType == VIEW_TYPE_ALBUM_CELL) { updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL); @@ -86,66 +71,12 @@ public class EntryGridAdapter extends RecyclerView.Adapter { updateView = new SongView(context); } - if(viewType != VIEW_TYPE_HEADER && updateView != null) { - final UpdateView view = updateView; - updateView.getChildAt(0).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Entry entry = getEntryForView(view); - - if (view.isCheckable() && view instanceof SongView) { - SongView songView = (SongView) view; - - if (selected.contains(entry)) { - selected.remove(entry); - songView.setChecked(false); - } else { - selected.add(entry); - songView.setChecked(true); - } - } else if (onEntryClickedListener != null) { - onEntryClickedListener.onEntryClicked(entry); - } - } - }); - updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - Entry entry = getEntryForView(view); - - setContextEntry(view, entry); - v.showContextMenu(); - return false; - } - }); - - View moreButton = updateView.findViewById(R.id.more_button); - if(moreButton != null) { - moreButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Entry entry = getEntryForView(view); - setContextEntry(view, entry); - v.showContextMenu(); - } - }); - } - } - return new UpdateViewHolder(updateView); } @Override - public void onBindViewHolder(UpdateViewHolder holder, int position) { - // Header already created - if(header != null && position == 0) { - return; - } - + public void onBindViewHolder(UpdateViewHolder holder, Entry entry, int viewType) { UpdateView view = holder.getUpdateView(); - - int viewType = getItemViewType(position); - Entry entry = getEntryForPosition(position); if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) { AlbumView albumView = (AlbumView) view; albumView.setShowArtist(showArtist); @@ -153,29 +84,25 @@ public class EntryGridAdapter extends RecyclerView.Adapter { } else if(viewType == VIEW_TYPE_SONG) { SongView songView = (SongView) view; songView.setObject(entry, checkable); - songView.setChecked(selected.contains(entry)); } - view.setPosition(position); } @Override - public int getItemCount() { - int size = entries.size(); - - if(header != null) { - size++; + public void setChecked(UpdateView updateView, boolean checked) { + if(updateView instanceof SongView) { + ((SongView) updateView).setChecked(checked); } + } - return size; + public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) { + return new UpdateViewHolder(header, false); } + public void onBindHeaderHolder(UpdateViewHolder holder, String header) { - @Override - public int getItemViewType(int position) { - if(header != null && position == 0) { - return VIEW_TYPE_HEADER; - } + } - Entry entry = getEntryForPosition(position); + @Override + public int getItemViewType(Entry entry) { if(entry.isDirectory()) { if (largeAlbums) { return VIEW_TYPE_ALBUM_CELL; @@ -187,20 +114,9 @@ public class EntryGridAdapter extends RecyclerView.Adapter { } } - public Entry getEntryForView(UpdateView view) { - int position = view.getPosition(); - return getEntryForPosition(position); - } - public Entry getEntryForPosition(int position) { - if(header != null) { - position--; - } - - return entries.get(position); - } - public void setHeader(View header) { this.header = header; + this.singleSectionHeader = true; } public void setShowArtist(boolean showArtist) { @@ -210,46 +126,8 @@ public class EntryGridAdapter extends RecyclerView.Adapter { this.checkable = checkable; } - public void setOnEntryClickedListener(OnEntryClickedListener listener) { - this.onEntryClickedListener = listener; - } - - public List getSelected() { - List selected = new ArrayList<>(); - selected.addAll(this.selected); - return selected; - } - public void clearSelected() { - for(Entry entry: selected) { - int index = entries.indexOf(entry); - this.notifyItemChanged(index); - } - selected.clear(); - } - - public void removeEntry(Entry entry) { - int index = entries.indexOf(entry); - if(index != -1) { - removeAt(index); - } - } public void removeAt(int index) { - entries.remove(index); + sections.get(0).remove(index); notifyItemRemoved(index); } - - public void setContextEntry(UpdateView view, Entry entry) { - this.contextView = view; - this.contextEntry = entry; - } - public UpdateView getContextView() { - return contextView; - } - public Entry getContextEntry() { - return contextEntry; - } - - public interface OnEntryClickedListener { - void onEntryClicked(Entry entry); - } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java index 288c38a6..7b7dc6fc 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java @@ -121,7 +121,7 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter { protected List cacheInBackground() throws Exception { MusicService service = MusicServiceFactory.getMusicService(context); MusicDirectory result; - int offset = entries.size(); + int offset = sections.get(0).size(); if(("genres".equals(type) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(type)) { result = service.getAlbumList(type, extra, size, offset, context, null); } else if("genres".equals(type) || "genres-songs".equals(type)) { @@ -134,13 +134,13 @@ public class EntryInfiniteGridAdapter extends EntryGridAdapter { protected void appendCachedData(List newData) { if(newData.size() > 0) { - int start = entries.size(); - entries.addAll(newData); + int start = sections.get(0).size(); + sections.get(0).addAll(newData); this.notifyItemRangeInserted(start, newData.size()); } } protected boolean isLoadingView(int position) { - return !allLoaded && position >= entries.size(); + return !allLoaded && position >= sections.get(0).size(); } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java new file mode 100644 index 00000000..5244c7a6 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java @@ -0,0 +1,280 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.view.BasicHeaderView; +import github.daneren2005.dsub.view.UpdateView; +import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder; + +public abstract class SectionAdapter extends RecyclerView.Adapter> { + private static String TAG = SectionAdapter.class.getSimpleName(); + public static int VIEW_TYPE_HEADER = 0; + + protected Context context; + protected List headers; + protected List> sections; + protected boolean singleSectionHeader; + protected OnItemClickedListener onItemClickedListener; + protected UpdateView contextView; + protected T contextItem; + private List selected = new ArrayList<>(); + + public SectionAdapter(Context context, List section) { + this(context, section, false); + } + public SectionAdapter(Context context, List section, boolean singleSectionHeader) { + this.context = context; + this.headers = Arrays.asList("Section"); + this.sections = new ArrayList<>(); + this.sections.add(section); + this.singleSectionHeader = singleSectionHeader; + } + public SectionAdapter(Context context, List headers, List> sections) { + this(context, headers, sections, true); + } + public SectionAdapter(Context context, List headers, List> sections, boolean singleSectionHeader){ + this.context = context; + this.headers = headers; + this.sections = sections; + this.singleSectionHeader = singleSectionHeader; + } + + @Override + public UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if(viewType == VIEW_TYPE_HEADER) { + return onCreateHeaderHolder(parent); + } else { + final UpdateViewHolder holder = onCreateSectionViewHolder(parent, viewType); + final UpdateView updateView = holder.getUpdateView(); + + if(updateView != null) { + updateView.getChildAt(0).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + T item = holder.getItem(); + if(updateView.isCheckable()) { + if (selected.contains(item)) { + selected.remove(item); + setChecked(updateView, false); + } else { + selected.add(item); + setChecked(updateView, true); + } + } else if(onItemClickedListener != null) { + onItemClickedListener.onItemClicked(item); + } + } + }); + updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + T item = holder.getItem(); + setContextItem(updateView, item); + v.showContextMenu(); + return false; + } + }); + + View moreButton = updateView.findViewById(R.id.more_button); + if(moreButton == null) { + moreButton = updateView.findViewById(R.id.item_more); + } + if (moreButton != null) { + moreButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + T item = holder.getItem(); + setContextItem(updateView, item); + v.showContextMenu(); + } + }); + } + } + + return holder; + } + } + + @Override + public void onBindViewHolder(UpdateViewHolder holder, int position) { + UpdateView updateView = holder.getUpdateView(); + + if(sections.size() == 1 && !singleSectionHeader) { + T item = sections.get(0).get(position); + onBindViewHolder(holder, item, getItemViewType(position)); + if(updateView.isCheckable()) { + setChecked(updateView, selected.contains(item)); + } + holder.setItem(item); + return; + } + + int subPosition = 0; + for(List section: sections) { + if(position == subPosition) { + int index = sections.indexOf(section); + onBindHeaderHolder(holder, headers.get(index)); + return; + } + + if(position <= (subPosition + section.size())) { + T item = section.get(position - subPosition - 1); + onBindViewHolder(holder, item, getItemViewType(item)); + + if(updateView.isCheckable()) { + setChecked(updateView, selected.contains(item)); + } + holder.setItem(item); + return; + } + + subPosition += section.size() + 1; + } + } + + @Override + public int getItemCount() { + if(sections.size() == 1 && !singleSectionHeader) { + return sections.get(0).size(); + } + + int count = headers.size(); + for(List section: sections) { + count += section.size(); + } + + return count; + } + + @Override + public int getItemViewType(int position) { + if(sections.size() == 1 && !singleSectionHeader) { + return getItemViewType(sections.get(0).get(position)); + } + + int subPosition = 0; + for(List section: sections) { + if(position == subPosition) { + return VIEW_TYPE_HEADER; + } + + if(position <= (subPosition + section.size())) { + return getItemViewType(section.get(position - subPosition - 1)); + } + + subPosition += section.size() + 1; + } + + return -1; + } + + public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) { + return new UpdateViewHolder(new BasicHeaderView(context)); + } + public void onBindHeaderHolder(UpdateViewHolder holder, String header) { + UpdateView view = holder.getUpdateView(); + view.setObject(header); + } + + public T getItemForPosition(int position) { + if(sections.size() == 1 && !singleSectionHeader) { + return sections.get(0).get(position); + } + + int subPosition = 0; + for(List section: sections) { + if(position == subPosition) { + return null; + } + + if(position <= (subPosition + section.size())) { + return section.get(position - subPosition - 1); + } + + subPosition += section.size() + 1; + } + + return null; + } + + public void setContextItem(UpdateView updateView, T item) { + contextView = updateView; + contextItem = item; + } + public UpdateView getContextView() { + return contextView; + } + public T getContextItem() { + return contextItem; + } + + public void setOnItemClickedListener(OnItemClickedListener onItemClickedListener) { + this.onItemClickedListener = onItemClickedListener; + } + + public List getSelected() { + List selected = new ArrayList<>(); + selected.addAll(this.selected); + return selected; + } + + public void clearSelected() { + // TODO: This needs to work with multiple sections + for(T item: selected) { + int index = sections.get(0).indexOf(item); + this.notifyItemChanged(index); + } + selected.clear(); + } + + public void removeItem(T item) { + int subPosition = 0; + for(List section: sections) { + if(sections.size() > 1 || singleSectionHeader) { + subPosition++; + } + + int index = section.indexOf(item); + if (index != -1) { + section.remove(item); + notifyItemRemoved(subPosition + index); + break; + } + + subPosition += section.size(); + } + } + + public abstract UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType); + public abstract void onBindViewHolder(UpdateViewHolder holder, T item, int viewType); + public abstract int getItemViewType(T item); + public void setChecked(UpdateView updateView, boolean checked) {} + + public interface OnItemClickedListener { + void onItemClicked(T item); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java index d427a26d..f21967b9 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -34,6 +34,7 @@ import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.adapter.EntryInfiniteGridAdapter; import github.daneren2005.dsub.adapter.EntryGridAdapter; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.ArtistInfo; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.ServerInfo; @@ -68,7 +69,7 @@ import java.util.Set; import static github.daneren2005.dsub.domain.MusicDirectory.Entry; -public class SelectDirectoryFragment extends SubsonicFragment implements EntryGridAdapter.OnEntryClickedListener { +public class SelectDirectoryFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener { private static final String TAG = SelectDirectoryFragment.class.getSimpleName(); private RecyclerView recyclerView; @@ -341,7 +342,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); - Entry entry = entryGridAdapter.getContextEntry(); + Entry entry = entryGridAdapter.getContextItem(); UpdateView targetView = entryGridAdapter.getContextView(); menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); @@ -378,7 +379,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr if(menuItem.getGroupId() != getSupportTag()) { return false; } - Entry entry = entryGridAdapter.getContextEntry(); + Entry entry = entryGridAdapter.getContextItem(); if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) { List songs = new ArrayList(); @@ -412,7 +413,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr } @Override - public void onEntryClicked(Entry entry) { + public void onItemClicked(Entry entry) { if (entry.isDirectory()) { SubsonicFragment fragment = new SelectDirectoryFragment(); Bundle args = new Bundle(); @@ -750,7 +751,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr } }); } - entryGridAdapter.setOnEntryClickedListener(this); + entryGridAdapter.setOnItemClickedListener(this); // Always show artist if this is not a artist we are viewing if(!artist) { entryGridAdapter.setShowArtist(true); @@ -1054,7 +1055,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr @Override protected void done(Void result) { - entryGridAdapter.removeEntry(episode); + entryGridAdapter.removeItem(episode); } @Override @@ -1115,7 +1116,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements EntryGr Util.toast(context, context.getResources().getString(R.string.starring_content_unstarred, Integer.toString(unstar.size()))); for(Entry entry: unstar) { - entryGridAdapter.removeEntry(entry); + entryGridAdapter.removeItem(entry); } } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java index c19e9b28..cb2ae6d7 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -26,32 +26,29 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; import github.daneren2005.dsub.util.BackgroundTask; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.TabBackgroundTask; -import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.view.GridSpacingDecoration; public abstract class SelectRecyclerFragment extends SubsonicFragment { private static final String TAG = SelectRecyclerFragment.class.getSimpleName(); protected RecyclerView recyclerView; - protected RecyclerView.Adapter adapter; + protected SectionAdapter adapter; protected BackgroundTask> currentTask; protected List objects; protected boolean serialize = true; - protected boolean largeCells; + protected boolean largeCells = false; protected int columns; @Override @@ -61,7 +58,6 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment { if(bundle != null && serialize) { objects = (List) bundle.getSerializable(Constants.FRAGMENT_LIST); } - largeCells = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true); columns = context.getResources().getInteger(R.integer.Grid_Columns); } @@ -89,6 +85,7 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment { } else { recyclerView.setAdapter(adapter = getAdapter(objects)); } + registerForContextMenu(recyclerView); return rootView; } @@ -190,7 +187,7 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment { } public abstract int getOptionsMenu(); - public abstract RecyclerView.Adapter getAdapter(List objs); + public abstract SectionAdapter getAdapter(List objs); public abstract List getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception; public abstract int getTitleResource(); diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java new file mode 100644 index 00000000..d8111692 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java @@ -0,0 +1,37 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.TextView; + +import github.daneren2005.dsub.R; + +public class BasicHeaderView extends UpdateView { + TextView nameView; + + public BasicHeaderView(Context context) { + super(context, false); + + LayoutInflater.from(context).inflate(R.layout.basic_header, this, true); + nameView = (TextView) findViewById(R.id.item_name); + } + + protected void setObjectImpl(Object obj) { + nameView.setText((String) obj); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java index bdcdc46f..905fbb06 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java @@ -66,7 +66,6 @@ public class UpdateView extends LinearLayout { protected SilentBackgroundTask imageTask = null; protected final boolean autoUpdate; - protected int position; protected boolean checkable; public UpdateView(Context context) { @@ -78,8 +77,8 @@ public class UpdateView extends LinearLayout { this.autoUpdate = autoUpdate; setLayoutParams(new AbsListView.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); if(autoUpdate) { INSTANCES.put(this, null); @@ -285,19 +284,13 @@ public class UpdateView extends LinearLayout { } } - public void setPosition(int position) { - this.position = position; - } - public int getPosition() { - return position; - } - public boolean isCheckable() { return checkable; } - public static class UpdateViewHolder extends RecyclerView.ViewHolder { + public static class UpdateViewHolder extends RecyclerView.ViewHolder { private UpdateView updateView; + private T item; public UpdateViewHolder(UpdateView itemView) { super(itemView); @@ -313,5 +306,11 @@ public class UpdateView extends LinearLayout { public UpdateView getUpdateView() { return updateView; } + public void setItem(T item) { + this.item = item; + } + public T getItem() { + return item; + } } } diff --git a/app/src/main/res/layout/basic_header.xml b/app/src/main/res/layout/basic_header.xml new file mode 100644 index 00000000..39f8722d --- /dev/null +++ b/app/src/main/res/layout/basic_header.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/basic_list_item.xml b/app/src/main/res/layout/basic_list_item.xml index 2338f7e0..4974f4f5 100644 --- a/app/src/main/res/layout/basic_list_item.xml +++ b/app/src/main/res/layout/basic_list_item.xml @@ -3,7 +3,7 @@ android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent"> + android:background="@drawable/abc_item_background_holo_light"> Starred \"%s\" Unstarred \"%s\" Failed to update \"%s\", please try later. - + + My Playlists + Shared Playlists Failed to grab list of playlists Added %1$s songs to \"%2$s\" Failed to update \"%s\", please try later. -- cgit v1.2.3 From 7606a374f95c079c2e88426c177515e05fda1e2b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 26 May 2015 18:04:35 -0700 Subject: #479 Separate personal from shared playlists --- .../daneren2005/dsub/adapter/PlaylistAdapter.java | 88 +++++++++------------- .../github/daneren2005/dsub/domain/Playlist.java | 15 ++++ .../dsub/fragments/SelectPlaylistFragment.java | 51 +++++++++---- .../dsub/service/parser/PlaylistsParser.java | 3 +- .../github/daneren2005/dsub/view/PlaylistView.java | 5 -- 5 files changed, 89 insertions(+), 73 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java index d56a6b97..fa00c1dd 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java @@ -1,70 +1,54 @@ /* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus - */ + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ package github.daneren2005.dsub.adapter; import android.content.Context; -import github.daneren2005.dsub.R; + +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import github.daneren2005.dsub.domain.Playlist; import github.daneren2005.dsub.view.PlaylistView; +import github.daneren2005.dsub.view.UpdateView; -import java.util.Collections; -import java.util.Comparator; - -/** - * @author Sindre Mehus - */ -public class PlaylistAdapter extends ArrayAdapter { +public class PlaylistAdapter extends SectionAdapter { + public static int VIEW_TYPE_PLAYLIST = 1; - private final Context activity; - - public PlaylistAdapter(Context activity, List Playlists) { - super(activity, R.layout.basic_list_item, Playlists); - this.activity = activity; + public PlaylistAdapter(Context context, List playlists, OnItemClickedListener listener) { + super(context, playlists); + this.onItemClickedListener = listener; + } + public PlaylistAdapter(Context context, List headers, List> sections, OnItemClickedListener listener) { + super(context, headers, sections); + this.onItemClickedListener = listener; } @Override - public View getView(int position, View convertView, ViewGroup parent) { - Playlist entry = getItem(position); - PlaylistView view; - if (convertView != null && convertView instanceof PlaylistView) { - view = (PlaylistView) convertView; - } else { - view = new PlaylistView(activity); - } - view.setObject(entry); - return view; + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new PlaylistView(context)); } - public static class PlaylistComparator implements Comparator { - @Override - public int compare(Playlist playlist1, Playlist playlist2) { - return playlist1.getName().compareToIgnoreCase(playlist2.getName()); - } - - public static List sort(List playlists) { - Collections.sort(playlists, new PlaylistComparator()); - return playlists; - } + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Playlist playlist, int viewType) { + holder.getUpdateView().setObject(playlist); + holder.setItem(playlist); + } + @Override + public int getItemViewType(Playlist playlist) { + return VIEW_TYPE_PLAYLIST; } } diff --git a/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java index 7cd820c0..99b85ce9 100644 --- a/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java +++ b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java @@ -19,6 +19,9 @@ package github.daneren2005.dsub.domain; import java.io.Serializable; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; /** * @author Sindre Mehus @@ -125,4 +128,16 @@ public class Playlist implements Serializable { Playlist playlist = (Playlist) o; return playlist.id.equals(this.id); } + + public static class PlaylistComparator implements Comparator { + @Override + public int compare(Playlist playlist1, Playlist playlist2) { + return playlist1.getName().compareToIgnoreCase(playlist2.getName()); + } + + public static List sort(List playlists) { + Collections.sort(playlists, new PlaylistComparator()); + return playlists; + } + } } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java index 3d7e664f..8939360c 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java @@ -2,18 +2,19 @@ package github.daneren2005.dsub.fragments; import android.app.AlertDialog; import android.content.DialogInterface; +import android.content.res.Resources; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; +import android.support.v7.widget.RecyclerView; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.EditText; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.Playlist; import github.daneren2005.dsub.domain.ServerInfo; @@ -31,9 +32,11 @@ import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.PlaylistAdapter; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -public class SelectPlaylistFragment extends SelectListFragment { +public class SelectPlaylistFragment extends SelectRecyclerFragment implements SectionAdapter.OnItemClickedListener { private static final String TAG = SelectPlaylistFragment.class.getSimpleName(); @Override @@ -47,8 +50,7 @@ public class SelectPlaylistFragment extends SelectListFragment { else { inflater.inflate(R.menu.select_playlist_context, menu); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Playlist playlist = (Playlist) listView.getItemAtPosition(info.position); + Playlist playlist = adapter.getContextItem(); if(SyncUtil.isSyncedPlaylist(context, playlist.getId())) { menu.removeItem(R.id.playlist_menu_sync); } else { @@ -71,9 +73,8 @@ public class SelectPlaylistFragment extends SelectListFragment { if(menuItem.getGroupId() != getSupportTag()) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Playlist playlist = (Playlist) listView.getItemAtPosition(info.position); + + Playlist playlist = adapter.getContextItem(); SubsonicFragment fragment; Bundle args; @@ -130,8 +131,31 @@ public class SelectPlaylistFragment extends SelectListFragment { } @Override - public ArrayAdapter getAdapter(List playlists) { - return new PlaylistAdapter(context, playlists); + public SectionAdapter getAdapter(List playlists) { + List mine = new ArrayList<>(); + List shared = new ArrayList<>(); + + String currentUsername = UserUtil.getCurrentUsername(context); + for(Playlist playlist: playlists) { + if(playlist.getOwner() == null || playlist.getOwner().equals(currentUsername)) { + mine.add(playlist); + } else { + shared.add(playlist); + } + } + + if(shared.isEmpty()) { + return new PlaylistAdapter(context, playlists, this); + } else { + Resources res = context.getResources(); + List headers = Arrays.asList(res.getString(R.string.playlist_mine), res.getString(R.string.playlist_shared)); + + List> sections = new ArrayList<>(); + sections.add(mine); + sections.add(shared); + + return new PlaylistAdapter(context, headers, sections, this); + } } @Override @@ -149,9 +173,7 @@ public class SelectPlaylistFragment extends SelectListFragment { } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Playlist playlist = (Playlist) parent.getItemAtPosition(position); - + public void onItemClicked(Playlist playlist) { SubsonicFragment fragment = new SelectDirectoryFragment(); Bundle args = new Bundle(); args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId()); @@ -179,7 +201,8 @@ public class SelectPlaylistFragment extends SelectListFragment { @Override protected void done(Void result) { - adapter.remove(playlist); + // TODO: Redo + // adapter.remove(playlist); adapter.notifyDataSetChanged(); Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName())); } diff --git a/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java index 6f01d510..69e5af64 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java +++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java @@ -22,7 +22,6 @@ import android.content.Context; import github.daneren2005.dsub.domain.Playlist; import github.daneren2005.dsub.util.ProgressListener; -import github.daneren2005.dsub.adapter.PlaylistAdapter; import org.xmlpull.v1.XmlPullParser; import java.io.Reader; @@ -64,7 +63,7 @@ public class PlaylistsParser extends AbstractParser { validate(); - return PlaylistAdapter.PlaylistComparator.sort(result); + return Playlist.PlaylistComparator.sort(result); } } \ No newline at end of file diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java index 25613984..29d5cc1c 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java @@ -50,11 +50,6 @@ public class PlaylistView extends UpdateView { starButton = (ImageButton) findViewById(R.id.item_star); starButton.setFocusable(false); moreButton = (ImageView) findViewById(R.id.item_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); } protected void setObjectImpl(Object obj) { -- cgit v1.2.3 From 507dbc4a002c2acc8b2577a84b0f0b70bc00aa33 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 27 May 2015 08:35:53 -0700 Subject: Put playlist removal after deletion back --- .../github/daneren2005/dsub/fragments/SelectPlaylistFragment.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java index 8939360c..03decc17 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java @@ -201,9 +201,7 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment imp @Override protected void done(Void result) { - // TODO: Redo - // adapter.remove(playlist); - adapter.notifyDataSetChanged(); + adapter.removeItem(playlist); Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName())); } -- cgit v1.2.3 From 3a65449a8e9b5298ffffb2fb59fd6113b0fdbb6b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 27 May 2015 08:39:42 -0700 Subject: Trigger loading a little earlier --- .../java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java index f21967b9..5dc50757 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -745,7 +745,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section return; } - if(totalItemCount > 0 && lastVisibleItem >= totalItemCount - 1) { + if(totalItemCount > 0 && lastVisibleItem >= totalItemCount - 2) { infiniteGridAdapter.loadMore(); } } -- cgit v1.2.3 From e285d60fa7e5115c1893279a1a264f205aadcb59 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 27 May 2015 08:42:46 -0700 Subject: Move OnItemClickListener into abstraction --- .../java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java | 2 +- .../java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java index 03decc17..2f2cfcc1 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java @@ -36,7 +36,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class SelectPlaylistFragment extends SelectRecyclerFragment implements SectionAdapter.OnItemClickedListener { +public class SelectPlaylistFragment extends SelectRecyclerFragment { private static final String TAG = SelectPlaylistFragment.class.getSimpleName(); @Override diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java index cb2ae6d7..afcbf273 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -41,7 +41,7 @@ import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.TabBackgroundTask; import github.daneren2005.dsub.view.GridSpacingDecoration; -public abstract class SelectRecyclerFragment extends SubsonicFragment { +public abstract class SelectRecyclerFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener { private static final String TAG = SelectRecyclerFragment.class.getSimpleName(); protected RecyclerView recyclerView; protected SectionAdapter adapter; -- cgit v1.2.3 From 50a5cee52fd0c0dd08a00ad59c2f9c2751e65834 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Thu, 28 May 2015 08:39:41 -0700 Subject: Convert a bunch of other fragments to RecyclerView --- .../daneren2005/dsub/adapter/BasicListAdapter.java | 48 +++++++++++++ .../daneren2005/dsub/adapter/BookmarkAdapter.java | 53 +++++++------- .../daneren2005/dsub/adapter/EntryGridAdapter.java | 2 +- .../daneren2005/dsub/adapter/GenreAdapter.java | 80 +++++++++------------- .../dsub/adapter/PodcastChannelAdapter.java | 79 +++++++++------------ .../daneren2005/dsub/adapter/SectionAdapter.java | 28 +++++--- .../daneren2005/dsub/adapter/ShareAdapter.java | 75 +++++++++----------- .../daneren2005/dsub/adapter/UserAdapter.java | 34 +++++---- .../daneren2005/dsub/fragments/AdminFragment.java | 14 ++-- .../dsub/fragments/SelectBookmarkFragment.java | 16 +++-- .../dsub/fragments/SelectGenreFragment.java | 40 +++++------ .../dsub/fragments/SelectPodcastsFragment.java | 57 ++++++--------- .../dsub/fragments/SelectRecyclerFragment.java | 9 ++- .../dsub/fragments/SelectShareFragment.java | 38 +++++----- .../dsub/fragments/SelectVideoFragment.java | 24 ++++--- .../dsub/fragments/SelectYearFragment.java | 36 ++++------ .../dsub/fragments/SubsonicFragment.java | 6 +- .../github/daneren2005/dsub/util/UserUtil.java | 6 +- .../daneren2005/dsub/view/BasicListView.java | 45 ++++++++++++ .../github/daneren2005/dsub/view/SongView.java | 2 +- app/src/main/res/layout/genre_list_item.xml | 2 +- app/src/main/res/layout/user_list_item.xml | 2 +- 22 files changed, 377 insertions(+), 319 deletions(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java create mode 100644 app/src/main/java/github/daneren2005/dsub/view/BasicListView.java (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java new file mode 100644 index 00000000..dfea91bf --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java @@ -0,0 +1,48 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.adapter; + +import android.content.Context; +import android.view.ViewGroup; + +import java.util.List; + +import github.daneren2005.dsub.view.BasicListView; +import github.daneren2005.dsub.view.UpdateView; + +public class BasicListAdapter extends SectionAdapter { + public static int VIEW_TYPE_LINE = 1; + + public BasicListAdapter(Context context, List strings, OnItemClickedListener listener) { + super(context, strings); + this.onItemClickedListener = listener; + } + + @Override + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new BasicListView(context)); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, String item, int viewType) { + holder.getUpdateView().setObject(item); + } + + @Override + public int getItemViewType(String item) { + return VIEW_TYPE_LINE; + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java index 26d3e16a..8335966d 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java @@ -1,20 +1,16 @@ /* - This file is part of Subsonic. - + This file is part of Subsonic. Subsonic is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - Subsonic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus + Copyright 2015 (C) Scott Jackson */ package github.daneren2005.dsub.adapter; @@ -22,9 +18,7 @@ package github.daneren2005.dsub.adapter; import android.content.Context; import java.util.List; -import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.TextView; import github.daneren2005.dsub.R; @@ -32,33 +26,36 @@ import github.daneren2005.dsub.domain.Bookmark; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.view.SongView; +import github.daneren2005.dsub.view.UpdateView; -public class BookmarkAdapter extends ArrayAdapter { +public class BookmarkAdapter extends SectionAdapter { private final static String TAG = BookmarkAdapter.class.getSimpleName(); - private Context activity; - public BookmarkAdapter(Context activity, List bookmarks) { - super(activity, android.R.layout.simple_list_item_1, bookmarks); - this.activity = activity; + public BookmarkAdapter(Context activity, List bookmarks, OnItemClickedListener listener) { + super(activity, bookmarks); + this.onItemClickedListener = listener; } - - public View getView(int position, View convertView, ViewGroup parent) { - MusicDirectory.Entry entry = getItem(position); - Bookmark bookmark = entry.getBookmark(); - SongView view; - if (convertView != null) { - view = (SongView) convertView; - } else { - view = new SongView(activity); - } - view.setObject(entry, false); - + @Override + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new SongView(context)); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, MusicDirectory.Entry item, int viewType) { + SongView songView = (SongView) holder.getUpdateView(); + Bookmark bookmark = item.getBookmark(); + + songView.setObject(item, false); + // Add current position to duration - TextView durationTextView = (TextView) view.findViewById(R.id.song_duration); + TextView durationTextView = (TextView) songView.findViewById(R.id.song_duration); String duration = durationTextView.getText().toString(); durationTextView.setText(Util.formatDuration(bookmark.getPosition() / 1000) + " / " + duration); - - return view; + } + + @Override + public int getItemViewType(MusicDirectory.Entry item) { + return EntryGridAdapter.VIEW_TYPE_SONG; } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java index 1a4d96a4..48b278ec 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java @@ -83,7 +83,7 @@ public class EntryGridAdapter extends SectionAdapter { albumView.setObject(entry, imageLoader); } else if(viewType == VIEW_TYPE_SONG) { SongView songView = (SongView) view; - songView.setObject(entry, checkable); + songView.setObject(entry, checkable && !entry.isVideo()); } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java index abb208c9..7e6954f9 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java @@ -1,60 +1,48 @@ /* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ - Copyright 2010 (C) Sindre Mehus - */ package github.daneren2005.dsub.adapter; -import android.widget.ArrayAdapter; -import android.widget.SectionIndexer; import android.content.Context; -import android.view.View; import android.view.ViewGroup; -import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.view.GenreView; +import github.daneren2005.dsub.view.UpdateView; import java.util.List; -import java.util.Set; -import java.util.LinkedHashSet; -import java.util.ArrayList; -/** - * @author Sindre Mehus -*/ -public class GenreAdapter extends ArrayAdapter{ - private Context activity; - private List genres; - - public GenreAdapter(Context context, List genres) { - super(context, android.R.layout.simple_list_item_1, genres); - this.activity = context; - this.genres = genres; +public class GenreAdapter extends SectionAdapter{ + public static int VIEW_TYPE_GENRE = 1; + + public GenreAdapter(Context context, List genres, OnItemClickedListener listener) { + super(context, genres); + this.onItemClickedListener = listener; } - + @Override - public View getView(int position, View convertView, ViewGroup parent) { - Genre genre = genres.get(position); - GenreView view; - if (convertView != null && convertView instanceof GenreView) { - view = (GenreView) convertView; - } else { - view = new GenreView(activity); - } - view.setObject(genre); - return view; - } + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new GenreView(context)); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Genre item, int viewType) { + holder.getUpdateView().setObject(item); + } + + @Override + public int getItemViewType(Genre item) { + return VIEW_TYPE_GENRE; + } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java index 8ee39a10..dc94178d 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java @@ -1,60 +1,47 @@ /* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus - */ + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ package github.daneren2005.dsub.adapter; -import android.widget.ArrayAdapter; -import android.widget.SectionIndexer; import android.content.Context; -import android.view.View; import android.view.ViewGroup; -import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.PodcastChannel; import github.daneren2005.dsub.view.PodcastChannelView; +import github.daneren2005.dsub.view.UpdateView; import java.util.List; -import java.util.Set; -import java.util.LinkedHashSet; -import java.util.ArrayList; -/** - * @author Sindre Mehus -*/ -public class PodcastChannelAdapter extends ArrayAdapter{ - private Context activity; - private List podcasts; +public class PodcastChannelAdapter extends SectionAdapter{ + public static int VIEW_TYPE_PODCAST = 1; - public PodcastChannelAdapter(Context context, List podcasts) { - super(context, android.R.layout.simple_list_item_1, podcasts); - this.activity = context; - this.podcasts = podcasts; + public PodcastChannelAdapter(Context context, List podcasts, OnItemClickedListener listener) { + super(context, podcasts); + this.onItemClickedListener = listener; } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - PodcastChannel podcast = podcasts.get(position); - PodcastChannelView view; - if (convertView != null && convertView instanceof PodcastChannelView) { - view = (PodcastChannelView) convertView; - } else { - view = new PodcastChannelView(activity); - } - view.setObject(podcast); - return view; + + @Override + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new PodcastChannelView(context)); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, PodcastChannel item, int viewType) { + holder.getUpdateView().setObject(item); + } + + @Override + public int getItemViewType(PodcastChannel item) { + return VIEW_TYPE_PODCAST; } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java index 5244c7a6..84b21e30 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java @@ -76,7 +76,7 @@ public abstract class SectionAdapter extends RecyclerView.Adapter extends RecyclerView.Adapter extends RecyclerView.Adapter extends RecyclerView.Adapter. + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ - Copyright 2010 (C) Sindre Mehus - */ package github.daneren2005.dsub.adapter; import android.content.Context; -import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import java.util.List; import github.daneren2005.dsub.domain.Share; import github.daneren2005.dsub.view.ShareView; +import github.daneren2005.dsub.view.UpdateView; -/** - * @author Sindre Mehus -*/ -public class ShareAdapter extends ArrayAdapter{ - private Context activity; - private List shares; - - public ShareAdapter(Context context, List shares) { - super(context, android.R.layout.simple_list_item_1, shares); - this.activity = context; - this.shares = shares; +public class ShareAdapter extends SectionAdapter{ + public static int VIEW_TYPE_SHARE = 1; + + public ShareAdapter(Context context, List shares, OnItemClickedListener listener) { + super(context, shares); + this.onItemClickedListener = listener; } - + @Override - public View getView(int position, View convertView, ViewGroup parent) { - Share share = shares.get(position); - ShareView view; - if (convertView != null && convertView instanceof ShareView) { - view = (ShareView) convertView; - } else { - view = new ShareView(activity); - } - view.setObject(share); - return view; - } + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new ShareView(context)); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Share item, int viewType) { + holder.getUpdateView().setObject(item); + } + + @Override + public int getItemViewType(Share item) { + return VIEW_TYPE_SHARE; + } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java index f0f78d97..95809e48 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java @@ -25,28 +25,32 @@ import java.util.List; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.util.ImageLoader; +import github.daneren2005.dsub.view.UpdateView; import github.daneren2005.dsub.view.UserView; -public class UserAdapter extends ArrayAdapter { - private final Context activity; +public class UserAdapter extends SectionAdapter { + public static int VIEW_TYPE_USER = 1; + private final ImageLoader imageLoader; - public UserAdapter(Context activity, List users, ImageLoader imageLoader) { - super(activity, R.layout.basic_list_item, users); - this.activity = activity; + public UserAdapter(Context context, List users, ImageLoader imageLoader, OnItemClickedListener listener) { + super(context, users); this.imageLoader = imageLoader; + this.onItemClickedListener = listener; + } + + @Override + public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) { + return new UpdateView.UpdateViewHolder(new UserView(context)); + } + + @Override + public void onBindViewHolder(UpdateView.UpdateViewHolder holder, User item, int viewType) { + holder.getUpdateView().setObject(item, imageLoader); } @Override - public View getView(int position, View convertView, ViewGroup parent) { - User entry = getItem(position); - UserView view; - if (convertView != null && convertView instanceof UserView) { - view = (UserView) convertView; - } else { - view = new UserView(activity); - } - view.setObject(entry, imageLoader); - return view; + public int getItemViewType(User item) { + return VIEW_TYPE_USER; } } \ No newline at end of file diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java index 66ce5f15..f3f9eb64 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.parser.SubsonicRESTException; @@ -37,7 +38,7 @@ import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.UserAdapter; -public class AdminFragment extends SelectListFragment { +public class AdminFragment extends SelectRecyclerFragment { private static String TAG = AdminFragment.class.getSimpleName(); @Override @@ -69,8 +70,7 @@ public class AdminFragment extends SelectListFragment { @Override public boolean onContextItemSelected(MenuItem menuItem) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - User user = objects.get(info.position); + User user = adapter.getContextItem(); switch(menuItem.getItemId()) { case R.id.admin_change_email: @@ -97,8 +97,8 @@ public class AdminFragment extends SelectListFragment { } @Override - public ArrayAdapter getAdapter(List objs) { - return new UserAdapter(context, objs, getImageLoader()); + public SectionAdapter getAdapter(List objs) { + return new UserAdapter(context, objs, getImageLoader(), this); } @Override @@ -134,9 +134,7 @@ public class AdminFragment extends SelectListFragment { } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - User user = (User) parent.getItemAtPosition(position); - + public void onItemClicked(User user) { SubsonicFragment fragment = new UserFragment(); Bundle args = new Bundle(); args.putSerializable(Constants.INTENT_EXTRA_NAME_ID, user); diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java index 830e2957..a774a287 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java @@ -25,6 +25,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.Bookmark; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.service.DownloadService; @@ -33,16 +34,19 @@ import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.BookmarkAdapter; +import github.daneren2005.dsub.view.UpdateView; import java.util.Arrays; import java.util.List; -public class SelectBookmarkFragment extends SelectListFragment { +public class SelectBookmarkFragment extends SelectRecyclerFragment { private static final String TAG = SelectBookmarkFragment.class.getSimpleName(); @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); MenuInflater inflater = context.getMenuInflater(); inflater.inflate(R.menu.select_bookmark_context, menu); @@ -52,8 +56,7 @@ public class SelectBookmarkFragment extends SelectListFragment bookmarks) { - return new BookmarkAdapter(context, bookmarks); + public SectionAdapter getAdapter(List bookmarks) { + return new BookmarkAdapter(context, bookmarks, this); } @Override @@ -92,13 +95,12 @@ public class SelectBookmarkFragment extends SelectListFragment parent, View view, int position, long id) { + public void onItemClicked(final MusicDirectory.Entry bookmark) { final DownloadService downloadService = getDownloadService(); if(downloadService == null) { return; } - final MusicDirectory.Entry bookmark = (MusicDirectory.Entry) parent.getItemAtPosition(position); new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java index 2d310172..fe012f62 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java @@ -1,21 +1,18 @@ /* - This file is part of Subsonic. + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus - */ package github.daneren2005.dsub.fragments; import android.os.Bundle; @@ -23,6 +20,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.util.Constants; @@ -31,7 +29,7 @@ import github.daneren2005.dsub.adapter.GenreAdapter; import java.util.List; -public class SelectGenreFragment extends SelectListFragment { +public class SelectGenreFragment extends SelectRecyclerFragment { private static final String TAG = SelectGenreFragment.class.getSimpleName(); @Override @@ -40,8 +38,8 @@ public class SelectGenreFragment extends SelectListFragment { } @Override - public ArrayAdapter getAdapter(List objs) { - return new GenreAdapter(context, objs); + public SectionAdapter getAdapter(List objs) { + return new GenreAdapter(context, objs, this); } @Override @@ -55,9 +53,7 @@ public class SelectGenreFragment extends SelectListFragment { } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Genre genre = (Genre) parent.getItemAtPosition(position); - + public void onItemClicked(Genre genre) { SubsonicFragment fragment = new SelectDirectoryFragment(); Bundle args = new Bundle(); args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "genres"); diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java index 3a564f1c..eebb1a8a 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java @@ -1,21 +1,17 @@ /* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus - */ + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ package github.daneren2005.dsub.fragments; import android.app.AlertDialog; @@ -24,10 +20,9 @@ import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.TextView; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.PodcastChannel; import github.daneren2005.dsub.service.MusicService; @@ -46,11 +41,7 @@ import github.daneren2005.dsub.adapter.PodcastChannelAdapter; import java.util.ArrayList; import java.util.List; -/** - * - * @author Scott - */ -public class SelectPodcastsFragment extends SelectListFragment { +public class SelectPodcastsFragment extends SelectRecyclerFragment { private static final String TAG = SelectPodcastsFragment.class.getSimpleName(); @Override @@ -79,8 +70,7 @@ public class SelectPodcastsFragment extends SelectListFragment { if(!Util.isOffline(context) && UserUtil.canPodcast()) { inflater.inflate(R.menu.select_podcasts_context, menu); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - PodcastChannel podcast = (PodcastChannel) listView.getItemAtPosition(info.position); + PodcastChannel podcast = adapter.getContextItem(); if(SyncUtil.isSyncedPodcast(context, podcast.getId())) { menu.removeItem(R.id.podcast_menu_sync); } else { @@ -98,10 +88,8 @@ public class SelectPodcastsFragment extends SelectListFragment { if(menuItem.getGroupId() != getSupportTag()) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - PodcastChannel channel = (PodcastChannel) listView.getItemAtPosition(info.position); + PodcastChannel channel = adapter.getContextItem(); switch (menuItem.getItemId()) { case R.id.podcast_menu_sync: syncPodcast(channel); @@ -126,8 +114,8 @@ public class SelectPodcastsFragment extends SelectListFragment { } @Override - public ArrayAdapter getAdapter(List channels) { - return new PodcastChannelAdapter(context, channels); + public SectionAdapter getAdapter(List channels) { + return new PodcastChannelAdapter(context, channels, this); } @Override @@ -141,9 +129,7 @@ public class SelectPodcastsFragment extends SelectListFragment { } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - PodcastChannel channel = (PodcastChannel) parent.getItemAtPosition(position); - + public void onItemClicked(PodcastChannel channel) { if("error".equals(channel.getStatus())) { Util.toast(context, context.getResources().getString(R.string.select_podcasts_invalid_podcast_channel, channel.getErrorMessage() == null ? "error" : channel.getErrorMessage())); } else if("downloading".equals(channel.getStatus())) { @@ -258,8 +244,7 @@ public class SelectPodcastsFragment extends SelectListFragment { @Override protected void done(Void result) { - adapter.remove(channel); - adapter.notifyDataSetChanged(); + adapter.removeItem(channel); Util.toast(context, context.getResources().getString(R.string.select_podcasts_deleted, channel.getName())); } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java index afcbf273..bd4d9526 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -12,6 +12,7 @@ along with Subsonic. If not, see . Copyright 2015 (C) Scott Jackson */ + package github.daneren2005.dsub.fragments; import android.os.Bundle; @@ -50,6 +51,7 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment impleme protected boolean serialize = true; protected boolean largeCells = false; protected int columns; + protected boolean pullToRefresh = true; @Override public void onCreate(Bundle bundle) { @@ -78,7 +80,12 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment impleme recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler); setupLayoutManager(); - setupScrollList(recyclerView); + + if(pullToRefresh) { + setupScrollList(recyclerView); + } else { + refreshLayout.setEnabled(false); + } if(objects == null) { refresh(false); diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java index 07cd3bef..87dd55b4 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java @@ -1,3 +1,18 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + package github.daneren2005.dsub.fragments; import android.app.AlertDialog; @@ -6,8 +21,6 @@ import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.DatePicker; @@ -17,6 +30,7 @@ import java.util.Date; import java.util.List; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.Share; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; @@ -28,10 +42,7 @@ import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.ShareAdapter; -/** - * Created by Scott on 12/28/13. - */ -public class SelectShareFragment extends SelectListFragment { +public class SelectShareFragment extends SelectRecyclerFragment { private static final String TAG = SelectShareFragment.class.getSimpleName(); @Override @@ -48,9 +59,7 @@ public class SelectShareFragment extends SelectListFragment { return false; } - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Share share = (Share) listView.getItemAtPosition(info.position); - + Share share = adapter.getContextItem(); switch (menuItem.getItemId()) { case R.id.share_menu_share: shareExternal(share); @@ -75,8 +84,8 @@ public class SelectShareFragment extends SelectListFragment { } @Override - public ArrayAdapter getAdapter(List objs) { - return new ShareAdapter(context, objs); + public SectionAdapter getAdapter(List objs) { + return new ShareAdapter(context, objs, this); } @Override @@ -90,9 +99,7 @@ public class SelectShareFragment extends SelectListFragment { } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Share share = (Share) parent.getItemAtPosition(position); - + public void onItemClicked(Share share) { SubsonicFragment fragment = new SelectDirectoryFragment(); Bundle args = new Bundle(); args.putSerializable(Constants.INTENT_EXTRA_NAME_SHARE, share); @@ -193,8 +200,7 @@ public class SelectShareFragment extends SelectListFragment { @Override protected void done(Void result) { - adapter.remove(share); - adapter.notifyDataSetChanged(); + adapter.removeItem(share); Util.toast(context, context.getResources().getString(R.string.share_deleted, share.getName())); } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java index b4d34ff9..e91a163c 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java @@ -24,20 +24,25 @@ import android.widget.ArrayAdapter; import java.util.List; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.EntryGridAdapter; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.adapter.EntryAdapter; +import github.daneren2005.dsub.view.UpdateView; -public class SelectVideoFragment extends SelectListFragment { +public class SelectVideoFragment extends SelectRecyclerFragment { @Override public int getOptionsMenu() { return R.menu.empty; } @Override - public ArrayAdapter getAdapter(List objs) { - return new EntryAdapter(context, null, objs, false); + public SectionAdapter getAdapter(List objs) { + SectionAdapter adapter = new EntryGridAdapter(context, objs, null, false); + adapter.setOnItemClickedListener(this); + return adapter; } @Override @@ -52,18 +57,17 @@ public class SelectVideoFragment extends SelectListFragment parent, View view, int position, long id) { - MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position); + public void onItemClicked(MusicDirectory.Entry entry) { playVideo(entry); } @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Object entry = listView.getItemAtPosition(info.position); - + MusicDirectory.Entry entry = adapter.getContextItem(); onCreateContextMenu(menu, view, menuInfo, entry); recreateContextMenu(menu); } @@ -74,9 +78,7 @@ public class SelectVideoFragment extends SelectListFragment. - - Copyright 2010 (C) Sindre Mehus + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson */ + package github.daneren2005.dsub.fragments; import android.os.Bundle; @@ -27,14 +24,13 @@ import java.util.ArrayList; import java.util.List; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.BasicListAdapter; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ProgressListener; -/** - * Created by Scott on 12/23/13. - */ -public class SelectYearFragment extends SelectListFragment { +public class SelectYearFragment extends SelectRecyclerFragment { @Override public int getOptionsMenu() { @@ -42,15 +38,15 @@ public class SelectYearFragment extends SelectListFragment { } @Override - public ArrayAdapter getAdapter(List objs) { - return new ArrayAdapter(context, android.R.layout.simple_list_item_1, objs); + public SectionAdapter getAdapter(List objs) { + return new BasicListAdapter(context, objs, this); } @Override - public List getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception { - List decades = new ArrayList(); + public List getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception { + List decades = new ArrayList<>(); for(int i = 2010; i >= 1920; i -= 10) { - decades.add(i); + decades.add(String.valueOf(i)); } return decades; @@ -62,15 +58,13 @@ public class SelectYearFragment extends SelectListFragment { } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Integer decade = (Integer) parent.getItemAtPosition(position); - + public void onItemClicked(String decade) { SubsonicFragment fragment = new SelectDirectoryFragment(); Bundle args = new Bundle(); args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "years"); args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20); args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0); - args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, Integer.toString(decade)); + args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, decade); fragment.setArguments(args); replaceFragment(fragment); diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java index 6070185d..82d5d97f 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java @@ -54,6 +54,7 @@ import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.activity.SubsonicActivity; import github.daneren2005.dsub.activity.SubsonicFragmentActivity; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.Artist; import github.daneren2005.dsub.domain.Bookmark; import github.daneren2005.dsub.domain.Genre; @@ -1627,7 +1628,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR }.execute(); } - protected void deleteBookmark(final MusicDirectory.Entry entry, final ArrayAdapter adapter) { + protected void deleteBookmark(final MusicDirectory.Entry entry, final SectionAdapter adapter) { Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -1653,8 +1654,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR @Override protected void done(Void result) { if (adapter != null) { - adapter.remove(entry); - adapter.notifyDataSetChanged(); + adapter.removeItem(entry); } Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle())); } diff --git a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java index 29618424..5c5f1543 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java +++ b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java @@ -28,6 +28,7 @@ import android.widget.ListView; import android.widget.TextView; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.fragments.SubsonicFragment; import github.daneren2005.dsub.service.DownloadService; @@ -326,7 +327,7 @@ public final class UserUtil { }); } - public static void deleteUser(final Context context, final User user, final ArrayAdapter adapter) { + public static void deleteUser(final Context context, final User user, final SectionAdapter adapter) { Util.confirmDialog(context, R.string.common_delete, user.getUsername(), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -341,8 +342,7 @@ public final class UserUtil { @Override protected void done(Void v) { if(adapter != null) { - adapter.remove(user); - adapter.notifyDataSetChanged(); + adapter.removeItem(user); } Util.toast(context, context.getResources().getString(R.string.admin_delete_user_success, user.getUsername())); diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java new file mode 100644 index 00000000..3169f903 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java @@ -0,0 +1,45 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import github.daneren2005.dsub.R; + +public class BasicListView extends UpdateView { + private TextView titleView; + + public BasicListView(Context context) { + super(context, false); + this.context = context; + LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); + + titleView = (TextView) findViewById(R.id.item_name); + starButton = (ImageButton) findViewById(R.id.item_star); + starButton.setFocusable(false); + moreButton = (ImageView) findViewById(R.id.item_more); + moreButton.setVisibility(View.GONE); + } + + protected void setObjectImpl(Object obj) { + titleView.setText((String) obj); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/view/SongView.java b/app/src/main/java/github/daneren2005/dsub/view/SongView.java index a5522719..49a09578 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/SongView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java @@ -140,7 +140,7 @@ public class SongView extends UpdateView implements Checkable { titleTextView.setText(title); artistTextView.setText(artist); - checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); + checkedTextView.setVisibility(checkable ? View.VISIBLE : View.GONE); this.setBackgroundColor(0x00000000); ratingBar.setVisibility(View.GONE); diff --git a/app/src/main/res/layout/genre_list_item.xml b/app/src/main/res/layout/genre_list_item.xml index 6affa24c..9a6e4245 100644 --- a/app/src/main/res/layout/genre_list_item.xml +++ b/app/src/main/res/layout/genre_list_item.xml @@ -3,7 +3,7 @@ android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent"> + android:background="@drawable/abc_item_background_holo_light"> + android:background="@drawable/abc_item_background_holo_light"> Date: Thu, 28 May 2015 17:58:08 -0700 Subject: Use ripple selector instead of basic holo one --- app/src/main/res/layout/album_cell_item.xml | 2 +- app/src/main/res/layout/basic_list_item.xml | 2 +- app/src/main/res/layout/genre_list_item.xml | 2 +- app/src/main/res/layout/song_list_item.xml | 2 +- app/src/main/res/layout/user_list_item.xml | 2 +- app/src/main/res/values/styles.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/res/layout/album_cell_item.xml b/app/src/main/res/layout/album_cell_item.xml index 46e2b1a6..fe634c13 100644 --- a/app/src/main/res/layout/album_cell_item.xml +++ b/app/src/main/res/layout/album_cell_item.xml @@ -3,7 +3,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/abc_item_background_holo_light"> + android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground">