aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2015-05-29 17:38:15 -0700
committerScott Jackson <daneren2005@gmail.com>2015-05-29 17:38:15 -0700
commit7111cf31fefa5136c62f39befa9fb4ee214d47ca (patch)
treee31e164e33efb85d8d604ce501860dc953f083f6 /app
parentcfa23075eb3c446fd22a67d782252781648e5dab (diff)
parent1697ead7480395a4850e2cfc06d2af2d58910d5a (diff)
downloaddsub-7111cf31fefa5136c62f39befa9fb4ee214d47ca.tar.gz
dsub-7111cf31fefa5136c62f39befa9fb4ee214d47ca.tar.bz2
dsub-7111cf31fefa5136c62f39befa9fb4ee214d47ca.zip
Merge branch 'RecyclerView' into SlideUpPanel2
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle3
-rw-r--r--app/src/main/AndroidManifest.xml2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java73
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java197
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java48
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java53
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java133
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java146
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java80
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java88
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java79
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java114
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java292
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java75
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java34
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Playlist.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java14
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java227
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java178
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java16
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java520
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java40
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java51
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java57
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java169
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java38
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java24
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java36
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java38
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java92
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java3
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/UserUtil.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java108
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumView.java52
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java37
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/BasicListView.java45
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java99
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java836
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SongView.java11
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java128
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UpdateView.java44
-rw-r--r--app/src/main/res/layout/abstract_recycler_fragment.xml27
-rw-r--r--app/src/main/res/layout/album_cell_item.xml5
-rw-r--r--app/src/main/res/layout/album_list_item.xml2
-rw-r--r--app/src/main/res/layout/basic_header.xml13
-rw-r--r--app/src/main/res/layout/basic_list_item.xml2
-rw-r--r--app/src/main/res/layout/genre_list_item.xml2
-rw-r--r--app/src/main/res/layout/grid_view.xml14
-rw-r--r--app/src/main/res/layout/select_album.xml5
-rw-r--r--app/src/main/res/layout/select_artist_header.xml4
-rw-r--r--app/src/main/res/layout/song_list_item.xml5
-rw-r--r--app/src/main/res/layout/unscrollable_grid_view.xml11
-rw-r--r--app/src/main/res/layout/user_list_item.xml2
-rw-r--r--app/src/main/res/values/strings.xml4
-rw-r--r--app/src/main/res/values/styles.xml2
58 files changed, 2087 insertions, 2320 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 56da71aa..1e2a6fe1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "github.daneren2005.dsub"
- minSdkVersion 9
+ minSdkVersion 14
targetSdkVersion 19
}
buildTypes {
@@ -32,6 +32,7 @@ dependencies {
compile 'com.android.support:support-v4:22.1.1'
compile 'com.android.support:appcompat-v7:22.1.1'
compile 'com.android.support:mediarouter-v7:22.1.1'
+ compile 'com.android.support:recyclerview-v7:22.1.1'
compile 'com.google.android.gms:play-services-cast:7.0.0'
compile 'com.sothree.slidinguppanel:library:3.0.0'
compile 'de.hdodenhof:circleimageview:1.2.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 38d80ae8..2b3a5fbc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -31,7 +31,7 @@
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<uses-feature android:name="android.hardware.wifi" android:required="false" />
- <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19"/>
<supports-screens android:anyDensity="true" android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index 4a0420ab..f3d62999 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -343,7 +343,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
if (query != null) {
((SearchFragment)currentFragment).search(query, autoplay);
} else {
- ((SearchFragment)currentFragment).populateList();
if (requestsearch) {
onSearchRequested();
}
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 <http://www.gnu.org/licenses/>.
- 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<MusicDirectory.Entry> {
- private final static String TAG = AlbumGridAdapter.class.getSimpleName();
- private final Context activity;
- private final ImageLoader imageLoader;
- private List<MusicDirectory.Entry> entries;
- private boolean showArtist;
-
- public AlbumGridAdapter(Context activity, ImageLoader imageLoader, List<MusicDirectory.Entry> 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/ArtistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
index 4d469faf..0563b38d 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
@@ -1,97 +1,128 @@
/*
- 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 <http://www.gnu.org/licenses/>.
+ 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.adapter;
import android.content.Context;
-import github.daneren2005.dsub.R;
-import java.util.List;
+import android.support.v7.widget.PopupMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.MusicFolder;
+import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class ArtistAdapter extends SectionAdapter<Artist> {
+ public static int VIEW_TYPE_ARTIST = 4;
+
+ private List<MusicFolder> musicFolders;
+ private OnMusicFolderChanged onMusicFolderChanged;
+
+ public ArtistAdapter(Context context, List<Artist> artists, OnItemClickedListener listener) {
+ this(context, artists, null, listener, null);
+ }
+
+ public ArtistAdapter(Context context, List<Artist> artists, List<MusicFolder> musicFolders, OnItemClickedListener onItemClickedListener, OnMusicFolderChanged onMusicFolderChanged) {
+ super(context, artists);
+ this.musicFolders = musicFolders;
+ this.onItemClickedListener = onItemClickedListener;
+ this.onMusicFolderChanged = onMusicFolderChanged;
+
+ if(musicFolders != null) {
+ this.singleSectionHeader = true;
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ final View header = LayoutInflater.from(context).inflate(R.layout.select_artist_header, parent, false);
+ header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PopupMenu popup = new PopupMenu(context, header);
+
+ popup.getMenu().add(R.string.select_artist_all_folders);
+ for (MusicFolder musicFolder : musicFolders) {
+ popup.getMenu().add(musicFolder.getName());
+ }
+
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if(item.getTitle().equals(musicFolder.getName())) {
+ if(onMusicFolderChanged != null) {
+ onMusicFolderChanged.onMusicFolderChanged(musicFolder);
+ }
+ return true;
+ }
+ }
+
+ if(onMusicFolderChanged != null) {
+ onMusicFolderChanged.onMusicFolderChanged(null);
+ }
+ return true;
+ }
+ });
+ popup.show();
+ }
+ });
+
+ return new UpdateView.UpdateViewHolder(header, false);
+ }
+ @Override
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header) {
+ TextView folderName = (TextView) holder.getView().findViewById(R.id.select_artist_folder_2);
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * @author Sindre Mehus
- */
-public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexer {
-
- private final Context activity;
-
- // Both arrays are indexed by section ID.
- private final Object[] sections;
- private final Integer[] positions;
-
- public ArtistAdapter(Context activity, List<Artist> artists) {
- super(activity, R.layout.basic_list_item, artists);
- this.activity = activity;
-
- Set<String> sectionSet = new LinkedHashSet<String>(30);
- List<Integer> positionList = new ArrayList<Integer>(30);
- for (int i = 0; i < artists.size(); i++) {
- Artist artist = artists.get(i);
- String index = artist.getIndex();
- if (!sectionSet.contains(index)) {
- sectionSet.add(index);
- positionList.add(i);
- }
- }
- sections = sectionSet.toArray(new Object[sectionSet.size()]);
- positions = positionList.toArray(new Integer[positionList.size()]);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Artist entry = getItem(position);
- ArtistView view;
- if (convertView != null && convertView instanceof ArtistView) {
- view = (ArtistView) convertView;
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ if(musicFolderId != null) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if (musicFolder.getId().equals(musicFolderId)) {
+ folderName.setText(musicFolder.getName());
+ break;
+ }
+ }
} else {
- view = new ArtistView(activity);
+ folderName.setText(R.string.select_artist_all_folders);
}
- view.setObject(entry);
- return view;
- }
-
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new ArtistView(context));
+ }
+
@Override
- public Object[] getSections() {
- return sections;
- }
-
- @Override
- public int getPositionForSection(int section) {
- section = Math.min(section, positions.length - 1);
- return positions[section];
- }
-
- @Override
- public int getSectionForPosition(int pos) {
- for (int i = 0; i < sections.length - 1; i++) {
- if (pos < positions[i + 1]) {
- return i;
- }
- }
- return sections.length - 1;
- }
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Artist item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Artist item) {
+ return VIEW_TYPE_ARTIST;
+ }
+
+ public interface OnMusicFolderChanged {
+ void onMusicFolderChanged(MusicFolder musicFolder);
+ }
}
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 <http://www.gnu.org/licenses/>.
+ 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<String> {
+ public static int VIEW_TYPE_LINE = 1;
+
+ public BasicListAdapter(Context context, List<String> 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 <http://www.gnu.org/licenses/>.
-
- 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<MusicDirectory.Entry> {
+public class BookmarkAdapter extends SectionAdapter<MusicDirectory.Entry> {
private final static String TAG = BookmarkAdapter.class.getSimpleName();
- private Context activity;
- public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> bookmarks) {
- super(activity, android.R.layout.simple_list_item_1, bookmarks);
- this.activity = activity;
+ public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> 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/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<MusicDirectory.Entry> {
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..48b278ec
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java
@@ -0,0 +1,133 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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 SectionAdapter<Entry> {
+ private static String TAG = EntryGridAdapter.class.getSimpleName();
+
+ public static int VIEW_TYPE_ALBUM_CELL = 1;
+ public static int VIEW_TYPE_ALBUM_LINE = 2;
+ public static int VIEW_TYPE_SONG = 3;
+
+ private ImageLoader imageLoader;
+ private boolean largeAlbums;
+ private boolean showArtist = false;
+ private boolean checkable = true;
+ private View header;
+
+ public EntryGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
+ super(context, 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 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);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ updateView = new SongView(context);
+ }
+
+ return new UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateViewHolder holder, Entry entry, int viewType) {
+ UpdateView view = holder.getUpdateView();
+ 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 && !entry.isVideo());
+ }
+ }
+
+ @Override
+ public void setChecked(UpdateView updateView, boolean checked) {
+ if(updateView instanceof SongView) {
+ ((SongView) updateView).setChecked(checked);
+ }
+ }
+
+ public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateViewHolder(header, false);
+ }
+ public void onBindHeaderHolder(UpdateViewHolder holder, String header) {
+
+ }
+
+ @Override
+ public int getItemViewType(Entry entry) {
+ if(entry.isDirectory()) {
+ if (largeAlbums) {
+ return VIEW_TYPE_ALBUM_CELL;
+ } else {
+ return VIEW_TYPE_ALBUM_LINE;
+ }
+ } else {
+ return VIEW_TYPE_SONG;
+ }
+ }
+
+ public void setHeader(View header) {
+ this.header = header;
+ this.singleSectionHeader = true;
+ }
+
+ public void setShowArtist(boolean showArtist) {
+ this.showArtist = showArtist;
+ }
+ public void setCheckable(boolean checkable) {
+ this.checkable = checkable;
+ }
+
+ public void removeAt(int index) {
+ sections.get(0).remove(index);
+ notifyItemRemoved(index);
+ }
+}
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..7b7dc6fc
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java
@@ -0,0 +1,146 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+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;
+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;
+
+ private boolean loading = false;
+ private boolean allLoaded = false;
+
+ public EntryInfiniteGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
+ 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;
+ this.size = size;
+ }
+
+ public void loadMore() {
+ if(loading || allLoaded) {
+ return;
+ }
+ loading = true;
+
+ new SilentBackgroundTask<Void>(context) {
+ private List<Entry> newData;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ newData = cacheInBackground();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ appendCachedData(newData);
+ loading = false;
+
+ if(newData.isEmpty()) {
+ allLoaded = true;
+ notifyDataSetChanged();
+ }
+ }
+ }.execute();
+ }
+
+ protected List<Entry> cacheInBackground() throws Exception {
+ MusicService service = MusicServiceFactory.getMusicService(context);
+ MusicDirectory result;
+ 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)) {
+ result = service.getSongsByGenre(extra, size, offset, context, null);
+ } else {
+ result = service.getAlbumList(type, size, offset, context, null);
+ }
+ return result.getChildren();
+ }
+
+ protected void appendCachedData(List<Entry> newData) {
+ if(newData.size() > 0) {
+ int start = sections.get(0).size();
+ sections.get(0).addAll(newData);
+ this.notifyItemRangeInserted(start, newData.size());
+ }
+ }
+
+ protected boolean isLoadingView(int position) {
+ return !allLoaded && position >= sections.get(0).size();
+ }
+}
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 <http://www.gnu.org/licenses/>.
+ 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 <http://www.gnu.org/licenses/>.
+ 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<Genre>{
- private Context activity;
- private List<Genre> genres;
-
- public GenreAdapter(Context context, List<Genre> genres) {
- super(context, android.R.layout.simple_list_item_1, genres);
- this.activity = context;
- this.genres = genres;
+public class GenreAdapter extends SectionAdapter<Genre>{
+ public static int VIEW_TYPE_GENRE = 1;
+
+ public GenreAdapter(Context context, List<Genre> 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/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 <http://www.gnu.org/licenses/>.
-
- 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 <http://www.gnu.org/licenses/>.
+ 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<Playlist> {
+public class PlaylistAdapter extends SectionAdapter<Playlist> {
+ public static int VIEW_TYPE_PLAYLIST = 1;
- private final Context activity;
-
- public PlaylistAdapter(Context activity, List<Playlist> Playlists) {
- super(activity, R.layout.basic_list_item, Playlists);
- this.activity = activity;
+ public PlaylistAdapter(Context context, List<Playlist> playlists, OnItemClickedListener listener) {
+ super(context, playlists);
+ this.onItemClickedListener = listener;
+ }
+ public PlaylistAdapter(Context context, List<String> headers, List<List<Playlist>> 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<Playlist> {
- @Override
- public int compare(Playlist playlist1, Playlist playlist2) {
- return playlist1.getName().compareToIgnoreCase(playlist2.getName());
- }
-
- public static List<Playlist> sort(List<Playlist> 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/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 <http://www.gnu.org/licenses/>.
-
- 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 <http://www.gnu.org/licenses/>.
+ 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<PodcastChannel>{
- private Context activity;
- private List<PodcastChannel> podcasts;
+public class PodcastChannelAdapter extends SectionAdapter<PodcastChannel>{
+ public static int VIEW_TYPE_PODCAST = 1;
- public PodcastChannelAdapter(Context context, List<PodcastChannel> podcasts) {
- super(context, android.R.layout.simple_list_item_1, podcasts);
- this.activity = context;
- this.podcasts = podcasts;
+ public PodcastChannelAdapter(Context context, List<PodcastChannel> 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/SearchAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java
new file mode 100644
index 00000000..f395216e
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java
@@ -0,0 +1,114 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.ViewGroup;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.domain.SearchResult;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.view.AlbumView;
+import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
+
+import static github.daneren2005.dsub.adapter.ArtistAdapter.VIEW_TYPE_ARTIST;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_CELL;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_LINE;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_SONG;
+
+public class SearchAdapter extends SectionAdapter<Serializable> {
+ private SearchResult searchResult;
+ private ImageLoader imageLoader;
+ private boolean largeAlbums;
+
+ public SearchAdapter(Context context, SearchResult searchResult, ImageLoader imageLoader, boolean largeAlbums, OnItemClickedListener listener) {
+ this.context = context;
+ this.searchResult = searchResult;
+ this.imageLoader = imageLoader;
+ this.largeAlbums = largeAlbums;
+
+ this.sections = new ArrayList<>();
+ this.headers = new ArrayList<>();
+ Resources res = context.getResources();
+ if(!searchResult.getArtists().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getArtists());
+ this.headers.add(res.getString(R.string.search_artists));
+ }
+ if(!searchResult.getAlbums().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getAlbums());
+ this.headers.add(res.getString(R.string.search_albums));
+ }
+ if(!searchResult.getSongs().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getSongs());
+ this.headers.add(res.getString(R.string.search_songs));
+ }
+ this.onItemClickedListener = listener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView = null;
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ updateView = new SongView(context);
+ } else if(viewType == VIEW_TYPE_ARTIST) {
+ updateView = new ArtistView(context);
+ }
+
+ return new UpdateView.UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Serializable item, int viewType) {
+ UpdateView view = holder.getUpdateView();
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ AlbumView albumView = (AlbumView) view;
+ albumView.setObject(item, imageLoader);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ SongView songView = (SongView) view;
+ songView.setObject(item, false);
+ } else if(viewType == VIEW_TYPE_ARTIST) {
+ view.setObject(item);
+ }
+ }
+
+ @Override
+ public int getItemViewType(Serializable item) {
+ if(item instanceof Entry) {
+ Entry entry = (Entry) item;
+ if (entry.isDirectory()) {
+ if (largeAlbums) {
+ return VIEW_TYPE_ALBUM_CELL;
+ } else {
+ return VIEW_TYPE_ALBUM_LINE;
+ }
+ } else {
+ return VIEW_TYPE_SONG;
+ }
+ } else {
+ return VIEW_TYPE_ARTIST;
+ }
+ }
+}
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..8d9a8682
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java
@@ -0,0 +1,292 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ 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<T> extends RecyclerView.Adapter<UpdateViewHolder<T>> {
+ private static String TAG = SectionAdapter.class.getSimpleName();
+ public static int VIEW_TYPE_HEADER = 0;
+
+ protected Context context;
+ protected List<String> headers;
+ protected List<List<T>> sections;
+ protected boolean singleSectionHeader;
+ protected OnItemClickedListener<T> onItemClickedListener;
+ protected UpdateView contextView;
+ protected T contextItem;
+ private List<T> selected = new ArrayList<>();
+
+ protected SectionAdapter() {}
+ public SectionAdapter(Context context, List<T> section) {
+ this(context, section, false);
+ }
+ public SectionAdapter(Context context, List<T> 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<String> headers, List<List<T>> sections) {
+ this(context, headers, sections, true);
+ }
+ public SectionAdapter(Context context, List<String> headers, List<List<T>> 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<T> 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);
+ }
+ }
+ });
+
+ 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();
+ }
+ });
+
+ /*updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ T item = holder.getItem();
+ setContextItem(updateView, item);
+ v.showContextMenu();
+ return false;
+ }
+ });*/
+ }
+ }
+
+ 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<T> 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<T> 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<T> 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();
+ if(view != null) {
+ view.setObject(header);
+ }
+ }
+
+ public T getItemForPosition(int position) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).get(position);
+ }
+
+ int subPosition = 0;
+ for(List<T> 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<T> onItemClickedListener) {
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ public void addSelected(T item) {
+ selected.add(item);
+ }
+ public List<T> getSelected() {
+ List<T> 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);
+
+ if(singleSectionHeader) {
+ index++;
+ }
+
+ this.notifyItemChanged(index);
+ }
+ selected.clear();
+ }
+
+ public void removeItem(T item) {
+ int subPosition = 0;
+ for(List<T> 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<T> {
+ void onItemClicked(T item);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
index 4121a85a..6db3d927 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
@@ -1,56 +1,49 @@
/*
- 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 <http://www.gnu.org/licenses/>.
+ 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 <http://www.gnu.org/licenses/>.
+ 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<Share>{
- private Context activity;
- private List<Share> shares;
-
- public ShareAdapter(Context context, List<Share> shares) {
- super(context, android.R.layout.simple_list_item_1, shares);
- this.activity = context;
- this.shares = shares;
+public class ShareAdapter extends SectionAdapter<Share>{
+ public static int VIEW_TYPE_SHARE = 1;
+
+ public ShareAdapter(Context context, List<Share> 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<User> {
- private final Context activity;
+public class UserAdapter extends SectionAdapter<User> {
+ public static int VIEW_TYPE_USER = 1;
+
private final ImageLoader imageLoader;
- public UserAdapter(Context activity, List<User> users, ImageLoader imageLoader) {
- super(activity, R.layout.basic_list_item, users);
- this.activity = activity;
+ public UserAdapter(Context context, List<User> 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/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<Playlist> {
+ @Override
+ public int compare(Playlist playlist1, Playlist playlist2) {
+ return playlist1.getName().compareToIgnoreCase(playlist2.getName());
+ }
+
+ public static List<Playlist> sort(List<Playlist> playlists) {
+ Collections.sort(playlists, new PlaylistComparator());
+ return playlists;
+ }
+ }
}
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<User> {
+public class AdminFragment extends SelectRecyclerFragment<User> {
private static String TAG = AdminFragment.class.getSimpleName();
@Override
@@ -69,8 +70,7 @@ public class AdminFragment extends SelectListFragment<User> {
@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<User> {
}
@Override
- public ArrayAdapter getAdapter(List<User> objs) {
- return new UserAdapter(context, objs, getImageLoader());
+ public SectionAdapter getAdapter(List<User> objs) {
+ return new UserAdapter(context, objs, getImageLoader(), this);
}
@Override
@@ -134,9 +134,7 @@ public class AdminFragment extends SelectListFragment<User> {
}
@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/SearchFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
index 0f1598dd..20a87e7d 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
@@ -1,12 +1,14 @@
package github.daneren2005.dsub.fragments;
-import java.util.ArrayList;
-import java.util.List;
+import java.io.Serializable;
import java.util.Arrays;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -14,11 +16,13 @@ import android.view.MenuInflater;
import android.view.View;
import android.view.MenuItem;
import android.widget.AdapterView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
import android.net.Uri;
import android.view.ViewGroup;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SearchAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.SearchCritera;
@@ -26,40 +30,24 @@ import github.daneren2005.dsub.domain.SearchResult;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
import github.daneren2005.dsub.util.BackgroundTask;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.adapter.EntryAdapter;
-import github.daneren2005.dsub.adapter.MergeAdapter;
import github.daneren2005.dsub.util.TabBackgroundTask;
import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.UpdateView;
-public class SearchFragment extends SubsonicFragment {
+public class SearchFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Serializable> {
private static final String TAG = SearchFragment.class.getSimpleName();
- private static final int DEFAULT_ARTISTS = 3;
- private static final int DEFAULT_ALBUMS = 5;
- private static final int DEFAULT_SONGS = 10;
-
private static final int MAX_ARTISTS = 10;
- private static final int MAX_ALBUMS = 20;
+ private static final int MAX_ALBUMS = 10;
private static final int MAX_SONGS = 25;
- private ListView list;
-
- private View artistsHeading;
- private View albumsHeading;
- private View songsHeading;
- private View moreArtistsButton;
- private View moreAlbumsButton;
- private View moreSongsButton;
+
+ protected RecyclerView recyclerView;
+ protected SearchAdapter adapter;
+ protected boolean largeAlbums = false;
+
private SearchResult searchResult;
- private MergeAdapter mergeAdapter;
- private ArtistAdapter artistAdapter;
- private ListAdapter moreArtistsAdapter;
- private EntryAdapter albumAdapter;
- private ListAdapter moreAlbumsAdapter;
- private ListAdapter moreSongsAdapter;
- private EntryAdapter songAdapter;
private boolean skipSearch = false;
private String currentQuery;
@@ -70,6 +58,7 @@ public class SearchFragment extends SubsonicFragment {
if(savedInstanceState != null) {
searchResult = (SearchResult) savedInstanceState.getSerializable(Constants.FRAGMENT_LIST);
}
+ largeAlbums = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true);
}
@Override
@@ -80,63 +69,43 @@ public class SearchFragment extends SubsonicFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
+ rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false);
setTitle(R.string.search_title);
- View buttons = inflater.inflate(R.layout.search_buttons, null);
-
- artistsHeading = buttons.findViewById(R.id.search_artists);
- albumsHeading = buttons.findViewById(R.id.search_albums);
- songsHeading = buttons.findViewById(R.id.search_songs);
-
- moreArtistsButton = buttons.findViewById(R.id.search_more_artists);
- moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
- moreSongsButton = buttons.findViewById(R.id.search_more_songs);
-
refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
refreshLayout.setEnabled(false);
- list = (ListView) rootView.findViewById(R.id.fragment_list);
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
+ setupLayoutManager(recyclerView, largeAlbums);
- list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == moreArtistsButton) {
- expandArtists();
- } else if (view == moreAlbumsButton) {
- expandAlbums();
- } else if (view == moreSongsButton) {
- expandSongs();
- } else {
- Object item = parent.getItemAtPosition(position);
- if (item instanceof Artist) {
- onArtistSelected((Artist) item, false);
- } else if (item instanceof MusicDirectory.Entry) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
- if (entry.isDirectory()) {
- onAlbumSelected(entry, false);
- } else if (entry.isVideo()) {
- onVideoSelected(entry);
- } else {
- onSongSelected(entry, false, true, true, false);
- }
-
- }
- }
- }
- });
- registerForContextMenu(list);
+ registerForContextMenu(recyclerView);
context.onNewIntent(context.getIntent());
if(searchResult != null) {
skipSearch = true;
- populateList();
+ recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, this));
}
return rootView;
}
@Override
+ public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() {
+ final int columns = getRecyclerColumnCount();
+ return new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ int viewType = adapter.getItemViewType(position);
+ if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == ArtistAdapter.VIEW_TYPE_ARTIST) {
+ return columns;
+ } else {
+ return 1;
+ }
+ }
+ };
+ }
+
+ @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.search, menu);
}
@@ -156,11 +125,12 @@ public class SearchFragment extends SubsonicFragment {
@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 selectedItem = list.getItemAtPosition(info.position);
- onCreateContextMenu(menu, view, menuInfo, selectedItem);
- if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
+ Serializable item = adapter.getContextItem();
+ onCreateContextMenu(menu, view, menuInfo, item);
+ if(item instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) item).isVideo() && !Util.isOffline(context)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
@@ -172,27 +142,37 @@ public class SearchFragment extends SubsonicFragment {
if(menuItem.getGroupId() != getSupportTag()) {
return false;
}
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem = list.getItemAtPosition(info.position);
-
- if(onContextItemSelected(menuItem, selectedItem)) {
+
+ Serializable item = adapter.getContextItem();
+ if(onContextItemSelected(menuItem, item)) {
return true;
}
return true;
}
-
- @Override
- public void setPrimaryFragment(boolean primary) {
- super.setPrimaryFragment(primary);
- }
@Override
public void refresh(boolean refresh) {
context.onNewIntent(context.getIntent());
}
+ @Override
+ public void onItemClicked(Serializable item) {
+ Log.d(TAG, item.getClass().getSimpleName());
+ if (item instanceof Artist) {
+ onArtistSelected((Artist) item, false);
+ } else if (item instanceof MusicDirectory.Entry) {
+ MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
+ if (entry.isDirectory()) {
+ onAlbumSelected(entry, false);
+ } else if (entry.isVideo()) {
+ onVideoSelected(entry);
+ } else {
+ onSongSelected(entry, false, true, true, false);
+ }
+ }
+ }
+
public void search(final String query, final boolean autoplay) {
if(skipSearch) {
skipSearch = false;
@@ -200,9 +180,6 @@ public class SearchFragment extends SubsonicFragment {
}
currentQuery = query;
- mergeAdapter = new MergeAdapter();
- list.setAdapter(mergeAdapter);
-
BackgroundTask<SearchResult> task = new TabBackgroundTask<SearchResult>(this) {
@Override
protected SearchResult doInBackground() throws Throwable {
@@ -214,7 +191,7 @@ public class SearchFragment extends SubsonicFragment {
@Override
protected void done(SearchResult result) {
searchResult = result;
- populateList();
+ recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, SearchFragment.this));
if (autoplay) {
autoplay(query);
}
@@ -224,82 +201,6 @@ public class SearchFragment extends SubsonicFragment {
task.execute();
}
- public void populateList() {
- mergeAdapter = new MergeAdapter();
-
- if (searchResult != null) {
- List<Artist> artists = searchResult.getArtists();
- if (!artists.isEmpty()) {
- mergeAdapter.addView(artistsHeading);
- List<Artist> displayedArtists = new ArrayList<Artist>(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size())));
- artistAdapter = new ArtistAdapter(context, displayedArtists);
- mergeAdapter.addAdapter(artistAdapter);
- if (artists.size() > DEFAULT_ARTISTS) {
- moreArtistsAdapter = mergeAdapter.addView(moreArtistsButton, true);
- }
- }
-
- List<MusicDirectory.Entry> albums = searchResult.getAlbums();
- if (!albums.isEmpty()) {
- mergeAdapter.addView(albumsHeading);
- List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size())));
- albumAdapter = new EntryAdapter(context, getImageLoader(), displayedAlbums, false);
- mergeAdapter.addAdapter(albumAdapter);
- if (albums.size() > DEFAULT_ALBUMS) {
- moreAlbumsAdapter = mergeAdapter.addView(moreAlbumsButton, true);
- }
- }
-
- List<MusicDirectory.Entry> songs = searchResult.getSongs();
- if (!songs.isEmpty()) {
- mergeAdapter.addView(songsHeading);
- List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size())));
- songAdapter = new EntryAdapter(context, getImageLoader(), displayedSongs, false);
- mergeAdapter.addAdapter(songAdapter);
- if (songs.size() > DEFAULT_SONGS) {
- moreSongsAdapter = mergeAdapter.addView(moreSongsButton, true);
- }
- }
-
- boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty();
- if(empty) {
- setEmpty(true);
- }
- }
-
- list.setAdapter(mergeAdapter);
- }
-
- private void expandArtists() {
- artistAdapter.clear();
- for (Artist artist : searchResult.getArtists()) {
- artistAdapter.add(artist);
- }
- artistAdapter.notifyDataSetChanged();
- mergeAdapter.removeAdapter(moreArtistsAdapter);
- mergeAdapter.notifyDataSetChanged();
- }
-
- private void expandAlbums() {
- albumAdapter.clear();
- for (MusicDirectory.Entry album : searchResult.getAlbums()) {
- albumAdapter.add(album);
- }
- albumAdapter.notifyDataSetChanged();
- mergeAdapter.removeAdapter(moreAlbumsAdapter);
- mergeAdapter.notifyDataSetChanged();
- }
-
- private void expandSongs() {
- songAdapter.clear();
- for (MusicDirectory.Entry song : searchResult.getSongs()) {
- songAdapter.add(song);
- }
- songAdapter.notifyDataSetChanged();
- mergeAdapter.removeAdapter(moreSongsAdapter);
- mergeAdapter.notifyDataSetChanged();
- }
-
private void onArtistSelected(Artist artist, boolean autoplay) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
index 5488c95b..065d622f 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
@@ -11,10 +11,11 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
-import android.widget.TextView;
+
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -23,19 +24,16 @@ import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.view.UpdateView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
-public class SelectArtistFragment extends SelectListFragment<Artist> {
+public class SelectArtistFragment extends SelectRecyclerFragment<Artist> implements ArtistAdapter.OnMusicFolderChanged {
private static final String TAG = SelectArtistFragment.class.getSimpleName();
private static final int MENU_GROUP_MUSIC_FOLDER = 10;
- private View folderButtonParent;
- private View folderButton;
- private TextView folderName;
private List<MusicFolder> musicFolders = null;
private List<MusicDirectory.Entry> entries;
private String groupId;
@@ -75,19 +73,7 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
}
- folderButton = null;
super.onCreateView(inflater, container, bundle);
-
- if("4.4.2".equals(Build.VERSION.RELEASE)) {
- listView.setFastScrollAlwaysVisible(true);
- }
-
- if(objects != null && currentTask == null) {
- if (Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
- folderButton.setVisibility(View.GONE);
- }
- setMusicFolders();
- }
return rootView;
}
@@ -95,30 +81,12 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
@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);
-
- if (entry instanceof Artist) {
- onCreateContextMenu(menu, view, menuInfo, entry);
- } else if (info.position == 0) {
- String musicFolderId = Util.getSelectedMusicFolderId(context);
- MenuItem menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders);
- if (musicFolderId == null) {
- menuItem.setChecked(true);
- }
- if (musicFolders != null) {
- for (int i = 0; i < musicFolders.size(); i++) {
- MusicFolder musicFolder = musicFolders.get(i);
- menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, musicFolder.getName());
- if (musicFolder.getId().equals(musicFolderId)) {
- menuItem.setChecked(true);
- }
- }
- }
- menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true);
- }
+ Artist artist = adapter.getContextItem();
+ onCreateContextMenu(menu, view, menuInfo, artist);
recreateContextMenu(menu);
}
@@ -129,56 +97,33 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Artist artist = (Artist) listView.getItemAtPosition(info.position);
-
- if (artist != null) {
- return onContextItemSelected(menuItem, artist);
- } else if (info.position == 0) {
- MusicFolder selectedFolder = menuItem.getItemId() == -1 ? null : musicFolders.get(menuItem.getItemId());
- String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
- String musicFolderName = selectedFolder == null ? context.getString(R.string.select_artist_all_folders)
- : selectedFolder.getName();
- Util.setSelectedMusicFolderId(context, musicFolderId);
- folderName.setText(musicFolderName);
- context.invalidate();
- }
+ Artist artist = adapter.getContextItem();
- return true;
+ return onContextItemSelected(menuItem, artist);
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == folderButtonParent) {
- selectFolder();
- } else {
- Artist artist = (Artist) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment;
- if((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || "root".equals(artist.getId()) || groupId != null) {
- fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- if ("root".equals(artist.getId())) {
- args.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
- }
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- fragment.setArguments(args);
- } else {
- fragment = new SelectArtistFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- fragment.setArguments(args);
+ public void onItemClicked(Artist artist) {
+ SubsonicFragment fragment;
+ if((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || "root".equals(artist.getId()) || groupId != null) {
+ fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ if ("root".equals(artist.getId())) {
+ args.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
}
-
- replaceFragment(fragment);
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+ } else {
+ fragment = new SelectArtistFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ fragment.setArguments(args);
}
- }
- @Override
- public void onFinishRefresh() {
- setMusicFolders();
+ replaceFragment(fragment);
}
@Override
@@ -215,9 +160,8 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- createMusicFolderButton();
- return new ArtistAdapter(context, objects);
+ public SectionAdapter getAdapter(List<Artist> objects) {
+ return new ArtistAdapter(context, objects, musicFolders, this, this);
}
@Override
@@ -236,12 +180,12 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
String musicFolderId = Util.getSelectedMusicFolderId(context);
Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
- artists = new ArrayList<Artist>(indexes.getShortcuts().size() + indexes.getArtists().size());
+ artists = new ArrayList<>(indexes.getShortcuts().size() + indexes.getArtists().size());
artists.addAll(indexes.getShortcuts());
artists.addAll(indexes.getArtists());
entries = indexes.getEntries();
} else {
- artists = new ArrayList<Artist>();
+ artists = new ArrayList<>();
MusicDirectory dir = musicService.getMusicDirectory(groupId, groupName, refresh, context, listener);
for(MusicDirectory.Entry entry: dir.getChildren(true, false)) {
Artist artist = new Artist();
@@ -251,7 +195,7 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
artists.add(artist);
}
- entries = new ArrayList<MusicDirectory.Entry>();
+ entries = new ArrayList<>();
entries.addAll(dir.getChildren(false, true));
if(!entries.isEmpty()) {
Artist root = new Artist();
@@ -270,32 +214,14 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
return groupId == null ? R.string.button_bar_browse : 0;
}
- private void createMusicFolderButton() {
- if(folderButton == null) {
- folderButtonParent = context.getLayoutInflater().inflate(R.layout.select_artist_header, listView, false);
- folderName = (TextView) folderButtonParent.findViewById(R.id.select_artist_folder_2);
- listView.addHeaderView(folderButtonParent);
- folderButton = folderButtonParent.findViewById(R.id.select_artist_folder);
- }
-
- if (Util.isOffline(context) || Util.isTagBrowsing(context) || musicFolders == null) {
- folderButton.setVisibility(View.GONE);
- } else {
- folderButton.setVisibility(View.VISIBLE);
- }
- }
-
@Override
public void setEmpty(boolean empty) {
super.setEmpty(empty);
if(empty && !Util.isOffline(context)) {
- createMusicFolderButton();
- setMusicFolders();
-
objects.clear();
- listView.setAdapter(new ArtistAdapter(context, objects));
- listView.setVisibility(View.VISIBLE);
+ recyclerView.setAdapter(new ArtistAdapter(context, objects, this));
+ recyclerView.setVisibility(View.VISIBLE);
View view = rootView.findViewById(R.id.tab_progress);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
@@ -305,29 +231,19 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
}
- private void setMusicFolders() {
- // Display selected music folder
- if (musicFolders != null) {
- String musicFolderId = Util.getSelectedMusicFolderId(context);
- if (musicFolderId == null) {
- folderName.setText(R.string.select_artist_all_folders);
- } else {
- for (MusicFolder musicFolder : musicFolders) {
- if (musicFolder.getId().equals(musicFolderId)) {
- folderName.setText(musicFolder.getName());
- break;
- }
- }
- }
- }
- }
-
- private void selectFolder() {
- folderButton.showContextMenu();
- }
-
private void toggleFirstLevelArtist() {
Util.toggleFirstLevelArtist(context);
context.invalidateOptionsMenu();
}
+
+ @Override
+ public void onMusicFolderChanged(MusicFolder selectedFolder) {
+ String startMusicFolderId = Util.getSelectedMusicFolderId(context);
+ String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
+
+ if(!Util.equals(startMusicFolderId, musicFolderId)) {
+ Util.setSelectedMusicFolderId(context, musicFolderId);
+ context.invalidate();
+ }
+ }
}
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<MusicDirectory.Entry> {
+public class SelectBookmarkFragment extends SelectRecyclerFragment<MusicDirectory.Entry> {
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<MusicDirectory.En
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- MusicDirectory.Entry bookmark = objects.get(info.position);
+ MusicDirectory.Entry bookmark = adapter.getContextItem();
switch(menuItem.getItemId()) {
case R.id.bookmark_menu_info:
@@ -77,8 +80,8 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
}
@Override
- public ArrayAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
- return new BookmarkAdapter(context, bookmarks);
+ public SectionAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
+ return new BookmarkAdapter(context, bookmarks, this);
}
@Override
@@ -92,13 +95,12 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
}
@Override
- public void onItemClick(AdapterView<?> 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<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
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..417b1328 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,21 @@ 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.adapter.SectionAdapter;
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 +58,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,17 +69,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 SectionAdapter.OnItemClickedListener<Entry> {
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<Entry> albums;
private List<Entry> entries;
- private boolean albumContext = false;
- private boolean addAlbumHeader = false;
private LoadTask currentTask;
private ArtistInfo artistInfo;
private String artistInfoDelayed;
@@ -113,7 +110,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
super.onCreate(bundle);
if(bundle != null) {
entries = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST);
- albums = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
artistInfo = (ArtistInfo) bundle.getSerializable(Constants.FRAGMENT_EXTRA);
restoredInstance = true;
}
@@ -171,28 +167,37 @@ 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;
- } 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);
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.select_album_entries);
+ recyclerView.setHasFixedSize(true);
+ setupScrollList(recyclerView);
- setupScrollList(albumList);
+ 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 || viewType == EntryInfiniteGridAdapter.VIEW_TYPE_LOADING) {
+ return columns;
+ } else {
+ return 1;
+ }
+ }
+ });
+ recyclerView.addItemDecoration(new GridSpacingDecoration());
+ recyclerView.setLayoutManager(gridLayoutManager);
+ } else {
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView.setLayoutManager(layoutManager);
}
- registerForContextMenu(entryList);
- setupAlbumList();
+
+ registerForContextMenu(recyclerView);
if(entries == null) {
if(primaryFragment || secondaryFragment) {
@@ -286,21 +291,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<Entry> 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 +338,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.getContextItem();
+ 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 +375,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.getContextItem();
if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) {
List<Entry> songs = new ArrayList<Entry>();
- 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 +389,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.<Integer>asList(info.position - headers));
+ removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>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 +409,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 onItemClicked(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.isAlbum()) {
+ 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 +456,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 +664,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,120 +702,120 @@ 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<Void>(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 - 2) {
+ infiniteGridAdapter.loadMore();
+ }
}
- }
- addAlbumHeader = false;
+ });
}
-
- boolean validData = !entries.isEmpty() || !albums.isEmpty();
- if(!validData) {
- setEmpty(true);
+ entryGridAdapter.setOnItemClickedListener(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
+ boolean addedHeader = false;
+ 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<Void>(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);
+ addedHeader = true;
}
}
- entryList.setAdapter(listAdapter);
- if(validData) {
- entryList.setVisibility(View.VISIBLE);
- }
- context.supportInvalidateOptionsMenu();
+ int scrollToPosition = -1;
if(lookupEntry != null) {
for(int i = 0; i < entries.size(); i++) {
if(lookupEntry.equals(entries.get(i).getTitle())) {
- entryList.setSelection(i + entryList.getHeaderViewsCount());
+ scrollToPosition = i;
+ entryGridAdapter.addSelected(entries.get(i));
lookupEntry = null;
break;
}
}
}
+ recyclerView.setAdapter(entryGridAdapter);
+ context.supportInvalidateOptionsMenu();
+
+ if(scrollToPosition != -1) {
+ recyclerView.scrollToPosition(scrollToPosition + (addedHeader ? 1 : 0));
+ }
+
Bundle args = getArguments();
boolean playAll = args.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
if (playAll && !restoredInstance) {
@@ -848,122 +823,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<Entry> 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<Entry> getSelectedSongs() {
- List<Entry> songs = new ArrayList<Entry>(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<Integer> getSelectedIndexes() {
+ List<Entry> selected = entryGridAdapter.getSelected();
List<Integer> indexes = new ArrayList<Integer>();
- 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<Entry> songs, final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle) {
if (getDownloadService() == null) {
return;
}
- final List<Entry> songs = getSelectedSongs();
warnIfStorageUnavailable();
// Conditions for using play now button
@@ -1006,11 +910,11 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
checkLicenseAndTrialPeriod(onValid);
}
private void downloadBackground(final boolean save) {
+ List<Entry> songs = getSelectedSongs();
if(playlistId != null) {
- selectAll(true, false);
+ songs = entries;
}
- List<Entry> songs = getSelectedSongs();
if(songs.isEmpty()) {
// Get both songs and albums
downloadRecursively(id, save, false, false, false, true);
@@ -1043,12 +947,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
private void delete() {
List<Entry> 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 +971,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 +1058,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
protected void done(Void result) {
- entries.remove(episode);
- entryAdapter.notifyDataSetChanged();
+ entryGridAdapter.removeItem(episode);
}
@Override
@@ -1218,10 +1119,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.removeItem(entry);
}
- entryAdapter.notifyDataSetChanged();
- selectAll(false, false);
}
@Override
@@ -1340,25 +1239,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 +1394,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/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 <http://www.gnu.org/licenses/>.
+ 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 <http://www.gnu.org/licenses/>.
-
- 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<Genre> {
+public class SelectGenreFragment extends SelectRecyclerFragment<Genre> {
private static final String TAG = SelectGenreFragment.class.getSimpleName();
@Override
@@ -40,8 +38,8 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
}
@Override
- public ArrayAdapter getAdapter(List<Genre> objs) {
- return new GenreAdapter(context, objs);
+ public SectionAdapter getAdapter(List<Genre> objs) {
+ return new GenreAdapter(context, objs, this);
}
@Override
@@ -55,9 +53,7 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
}
@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/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
index 3d7e664f..2f2cfcc1 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<Playlist> {
+public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
private static final String TAG = SelectPlaylistFragment.class.getSimpleName();
@Override
@@ -47,8 +50,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
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<Playlist> {
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<Playlist> {
}
@Override
- public ArrayAdapter getAdapter(List<Playlist> playlists) {
- return new PlaylistAdapter(context, playlists);
+ public SectionAdapter<Playlist> getAdapter(List<Playlist> playlists) {
+ List<Playlist> mine = new ArrayList<>();
+ List<Playlist> 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<String> headers = Arrays.asList(res.getString(R.string.playlist_mine), res.getString(R.string.playlist_shared));
+
+ List<List<Playlist>> 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<Playlist> {
}
@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,8 +201,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
@Override
protected void done(Void result) {
- adapter.remove(playlist);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(playlist);
Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
}
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 <http://www.gnu.org/licenses/>.
-
- 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 <http://www.gnu.org/licenses/>.
+ 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<PodcastChannel> {
+public class SelectPodcastsFragment extends SelectRecyclerFragment<PodcastChannel> {
private static final String TAG = SelectPodcastsFragment.class.getSimpleName();
@Override
@@ -79,8 +70,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
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<PodcastChannel> {
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<PodcastChannel> {
}
@Override
- public ArrayAdapter getAdapter(List<PodcastChannel> channels) {
- return new PodcastChannelAdapter(context, channels);
+ public SectionAdapter getAdapter(List<PodcastChannel> channels) {
+ return new PodcastChannelAdapter(context, channels, this);
}
@Override
@@ -141,9 +129,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
@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<PodcastChannel> {
@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
new file mode 100644
index 00000000..526a4312
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java
@@ -0,0 +1,169 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ 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.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 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;
+
+public abstract class SelectRecyclerFragment<T> extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<T> {
+ private static final String TAG = SelectRecyclerFragment.class.getSimpleName();
+ protected RecyclerView recyclerView;
+ protected SectionAdapter<T> adapter;
+ protected BackgroundTask<List<T>> currentTask;
+ protected List<T> objects;
+ protected boolean serialize = true;
+ protected boolean largeAlbums = false;
+ protected int columns;
+ protected boolean pullToRefresh = true;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null && serialize) {
+ objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
+ }
+ 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();
+
+ if(pullToRefresh) {
+ setupScrollList(recyclerView);
+ } else {
+ refreshLayout.setEnabled(false);
+ }
+
+ if(objects == null) {
+ refresh(false);
+ } else {
+ recyclerView.setAdapter(adapter = getAdapter(objects));
+ }
+ registerForContextMenu(recyclerView);
+
+ 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<List<T>>(this) {
+ @Override
+ protected List<T> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+
+ objects = new ArrayList<T>();
+
+ try {
+ objects = getObjects(musicService, refresh, this);
+ } catch (Exception x) {
+ Log.e(TAG, "Failed to load", x);
+ }
+
+ return objects;
+ }
+
+ @Override
+ protected void done(List<T> result) {
+ if (result != null && !result.isEmpty()) {
+ recyclerView.setAdapter(adapter = getAdapter(result));
+
+ onFinishRefresh();
+ recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ setEmpty(true);
+ }
+
+ currentTask = null;
+ }
+ };
+ currentTask.execute();
+ }
+
+ private void setupLayoutManager() {
+ setupLayoutManager(recyclerView, largeAlbums);
+ }
+
+ public abstract int getOptionsMenu();
+ public abstract SectionAdapter<T> getAdapter(List<T> objs);
+ public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
+ public abstract int getTitleResource();
+
+ public void onFinishRefresh() {
+
+ }
+}
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 <http://www.gnu.org/licenses/>.
+ 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<Share> {
+public class SelectShareFragment extends SelectRecyclerFragment<Share> {
private static final String TAG = SelectShareFragment.class.getSimpleName();
@Override
@@ -48,9 +59,7 @@ public class SelectShareFragment extends SelectListFragment<Share> {
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<Share> {
}
@Override
- public ArrayAdapter getAdapter(List<Share> objs) {
- return new ShareAdapter(context, objs);
+ public SectionAdapter getAdapter(List<Share> objs) {
+ return new ShareAdapter(context, objs, this);
}
@Override
@@ -90,9 +99,7 @@ public class SelectShareFragment extends SelectListFragment<Share> {
}
@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<Share> {
@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<MusicDirectory.Entry> {
+public class SelectVideoFragment extends SelectRecyclerFragment<MusicDirectory.Entry> {
@Override
public int getOptionsMenu() {
return R.menu.empty;
}
@Override
- public ArrayAdapter getAdapter(List<MusicDirectory.Entry> objs) {
- return new EntryAdapter(context, null, objs, false);
+ public SectionAdapter getAdapter(List<MusicDirectory.Entry> objs) {
+ SectionAdapter adapter = new EntryGridAdapter(context, objs, null, false);
+ adapter.setOnItemClickedListener(this);
+ return adapter;
}
@Override
@@ -52,18 +57,17 @@ public class SelectVideoFragment extends SelectListFragment<MusicDirectory.Entry
}
@Override
- public void onItemClick(AdapterView<?> 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<MusicDirectory.Entry
return false;
}
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object entry = listView.getItemAtPosition(info.position);
-
+ MusicDirectory.Entry entry = adapter.getContextItem();
return onContextItemSelected(menuItem, entry);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
index dc19acad..a89bc280 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.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
+ 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+ 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<Integer> {
+public class SelectYearFragment extends SelectRecyclerFragment<String> {
@Override
public int getOptionsMenu() {
@@ -42,15 +38,15 @@ public class SelectYearFragment extends SelectListFragment<Integer> {
}
@Override
- public ArrayAdapter getAdapter(List<Integer> objs) {
- return new ArrayAdapter<Integer>(context, android.R.layout.simple_list_item_1, objs);
+ public SectionAdapter getAdapter(List<String> objs) {
+ return new BasicListAdapter(context, objs, this);
}
@Override
- public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- List<Integer> decades = new ArrayList<Integer>();
+ public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ List<String> 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<Integer> {
}
@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/SimilarArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
index 79e759cc..088b6d00 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
@@ -17,12 +17,15 @@ package github.daneren2005.dsub.fragments;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -32,13 +35,13 @@ import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.view.UpdateView;
import java.net.URLEncoder;
import java.util.LinkedList;
import java.util.List;
-public class SimilarArtistFragment extends SelectListFragment<Artist> {
+public class SimilarArtistFragment extends SelectRecyclerFragment<Artist> {
private static final String TAG = SimilarArtistFragment.class.getSimpleName();
private ArtistInfo info;
private String artistId;
@@ -52,6 +55,18 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ if(!primaryFragment) {
+ return;
+ }
+
+ if(info.getMissingArtists().isEmpty()) {
+ menu.removeItem(R.id.menu_show_missing);
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_play_now:
@@ -71,10 +86,11 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
@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);
- onCreateContextMenu(menu, view, menuInfo, entry);
+ Artist artist = adapter.getContextItem();
+ onCreateContextMenu(menu, view, menuInfo, artist);
recreateContextMenu(menu);
}
@@ -85,14 +101,12 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
return false;
}
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Artist artist = (Artist) listView.getItemAtPosition(info.position);
+ Artist artist = adapter.getContextItem();
return onContextItemSelected(menuItem, artist);
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Artist artist = (Artist) parent.getItemAtPosition(position);
+ public void onItemClicked(Artist artist) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
@@ -109,8 +123,8 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- return new ArtistAdapter(context, objects);
+ public SectionAdapter getAdapter(List<Artist> objects) {
+ return new ArtistAdapter(context, objects, this);
}
@Override
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..cc9f8d62 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,9 @@ 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.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.ContextMenu;
import android.view.GestureDetector;
@@ -53,6 +56,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;
@@ -76,10 +80,10 @@ 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;
+import github.daneren2005.dsub.view.GridSpacingDecoration;
import github.daneren2005.dsub.view.PlaylistSongView;
import github.daneren2005.dsub.view.SongView;
import github.daneren2005.dsub.view.UpdateView;
@@ -308,14 +312,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 +632,68 @@ 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);
+ }
+ }
+
+ public void setupLayoutManager(RecyclerView recyclerView, boolean largeAlbums) {
+ recyclerView.setLayoutManager(getLayoutManager(recyclerView, largeAlbums));
+ }
+ public RecyclerView.LayoutManager getLayoutManager(RecyclerView recyclerView, boolean largeCells) {
+ if(largeCells) {
+ return getGridLayoutManager(recyclerView);
+ } else {
+ return getLinearLayoutManager();
+ }
+ }
+ public GridLayoutManager getGridLayoutManager(RecyclerView recyclerView) {
+ final int columns = getRecyclerColumnCount();
+ 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;
+ }
+ public RecyclerView.ItemDecoration getItemDecoration() {
+ return new GridSpacingDecoration();
+ }
+ public int getRecyclerColumnCount() {
+ return context.getResources().getInteger(R.integer.Grid_Columns);
+ }
protected void warnIfStorageUnavailable() {
if (!Util.isExternalStoragePresent()) {
@@ -1608,7 +1670,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) {
@@ -1634,8 +1696,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()));
}
@@ -1788,11 +1849,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 +1862,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/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/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/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 <http://www.gnu.org/licenses/>.
- 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..69f2ef13 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,52 @@ 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();
- }
- });
+ 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/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 <http://www.gnu.org/licenses/>.
+ 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/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 <http://www.gnu.org/licenses/>.
+ 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/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 <http://www.gnu.org/licenses/>.
+ 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/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;
- /**
- * <code>true</code> 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<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
- private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>();
-
- 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.
- * <p/>
- * 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.
- * <p/>
- * 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<FixedViewInfo> 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.
- * <p>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<FixedViewInfo> EMPTY_INFO_LIST =
- new ArrayList<FixedViewInfo>();
-
- // This ArrayList is assumed to NOT be null.
- ArrayList<FixedViewInfo> mHeaderViewInfos;
- ArrayList<FixedViewInfo> 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<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> 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<FixedViewInfo> 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/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) {
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..49a09578 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);
@@ -145,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/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..62d1672d 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,7 @@ public class UpdateView extends LinearLayout {
protected SilentBackgroundTask<Void> imageTask = null;
protected final boolean autoUpdate;
+ protected boolean checkable;
public UpdateView(Context context) {
this(context, true);
@@ -75,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);
@@ -224,8 +226,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 +283,40 @@ public class UpdateView extends LinearLayout {
rating = isRated;
}
}
+
+ public boolean isCheckable() {
+ return checkable;
+ }
+
+ public static class UpdateViewHolder<T> extends RecyclerView.ViewHolder {
+ private UpdateView updateView;
+ private View view;
+ private T item;
+
+ public UpdateViewHolder(UpdateView itemView) {
+ super(itemView);
+
+ this.updateView = itemView;
+ this.view = itemView;
+ }
+
+ // Different is so that call is not ambiguous
+ public UpdateViewHolder(View view, boolean different) {
+ super(view);
+ this.view = view;
+ }
+
+ public UpdateView getUpdateView() {
+ return updateView;
+ }
+ public View getView() {
+ return view;
+ }
+ public void setItem(T item) {
+ this.item = item;
+ }
+ public T getItem() {
+ return item;
+ }
+ }
}
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..4c7dd6b8
--- /dev/null
+++ b/app/src/main/res/layout/abstract_recycler_fragment.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/refresh_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:id="@+id/fragment_list_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/fragment_recycler"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:scrollbars="vertical"/>
+
+ <include layout="@layout/tab_progress" />
+ </LinearLayout>
+</android.support.v4.widget.SwipeRefreshLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/album_cell_item.xml b/app/src/main/res/layout/album_cell_item.xml
index 3f708e63..fe634c13 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 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="?attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
@@ -77,7 +78,7 @@
</LinearLayout>
<ImageView
- android:id="@+id/album_more"
+ android:id="@+id/more_button"
android:src="?attr/download_none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/album_list_item.xml b/app/src/main/res/layout/album_list_item.xml
index 0ee92edd..67c052eb 100644
--- a/app/src/main/res/layout/album_list_item.xml
+++ b/app/src/main/res/layout/album_list_item.xml
@@ -65,7 +65,7 @@
android:visibility="gone"/>
<ImageView
- android:id="@+id/album_more"
+ android:id="@+id/more_button"
android:src="?attr/download_none"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
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..b5ae900a
--- /dev/null
+++ b/app/src/main/res/layout/basic_header.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item_name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@android:color/transparent"
+ android:textColor="@color/cyan"
+ android:textStyle="bold"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/> \ 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..88d8ca20 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="?attr/selectableItemBackground">
<TextView
android:id="@+id/item_name"
diff --git a/app/src/main/res/layout/genre_list_item.xml b/app/src/main/res/layout/genre_list_item.xml
index 6affa24c..a6a34c52 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="?attr/selectableItemBackground">
<TextView
android:id="@+id/genre_name"
diff --git a/app/src/main/res/layout/grid_view.xml b/app/src/main/res/layout/grid_view.xml
deleted file mode 100644
index 599cf92c..00000000
--- a/app/src/main/res/layout/grid_view.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<github.daneren2005.dsub.view.HeaderGridView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridview"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"
- android:numColumns="@integer/Grid.Columns"
- android:horizontalSpacing="10dp"
- android:verticalSpacing="10dp"
- android:gravity="center"
- android:stretchMode="columnWidth"
- android:padding="24px"
- android:fastScrollEnabled="true"
- android:scrollbarStyle="outsideOverlay"/> \ 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 @@
<include layout="@layout/tab_progress"/>
- <ListView
+ <android.support.v7.widget.RecyclerView
android:id="@+id/select_album_entries"
- android:textFilterEnabled="true"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
- android:fastScrollEnabled="true"/>
+ android:scrollbars="vertical"/>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/select_artist_header.xml b/app/src/main/res/layout/select_artist_header.xml
index 2821ce43..0ac9e6a6 100644
--- a/app/src/main/res/layout/select_artist_header.xml
+++ b/app/src/main/res/layout/select_artist_header.xml
@@ -2,7 +2,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground">
+
<LinearLayout
android:id="@+id/select_artist_folder"
android:orientation="horizontal"
diff --git a/app/src/main/res/layout/song_list_item.xml b/app/src/main/res/layout/song_list_item.xml
index 86f77869..9be27192 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="?attr/selectableItemBackground">
<CheckedTextView
android:id="@+id/song_check"
@@ -117,7 +118,7 @@
</LinearLayout>
<ImageView
- android:id="@+id/artist_more"
+ android:id="@+id/more_button"
android:src="?attr/download_none"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
diff --git a/app/src/main/res/layout/unscrollable_grid_view.xml b/app/src/main/res/layout/unscrollable_grid_view.xml
deleted file mode 100644
index 96bea5ce..00000000
--- a/app/src/main/res/layout/unscrollable_grid_view.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<github.daneren2005.dsub.view.UnscrollableGridView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridview"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:numColumns="@integer/Grid.Columns"
- android:horizontalSpacing="10dp"
- android:verticalSpacing="10dp"
- android:gravity="center"
- android:padding="20px"
- android:stretchMode="columnWidth"/> \ No newline at end of file
diff --git a/app/src/main/res/layout/user_list_item.xml b/app/src/main/res/layout/user_list_item.xml
index dc2bdab9..aa2d13c9 100644
--- a/app/src/main/res/layout/user_list_item.xml
+++ b/app/src/main/res/layout/user_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="?attr/selectableItemBackground">
<github.daneren2005.dsub.view.RecyclingImageView
android:id="@+id/item_avatar"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e6e19b4c..bb0a997b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -236,7 +236,9 @@
<string name="starring_content_starred">Starred \"%s\"</string>
<string name="starring_content_unstarred">Unstarred \"%s\"</string>
<string name="starring_content_error">Failed to update \"%s\", please try later.</string>
-
+
+ <string name="playlist.mine">My Playlists</string>
+ <string name="playlist.shared">Shared Playlists</string>
<string name="playlist_error">Failed to grab list of playlists</string>
<string name="updated_playlist">Added %1$s songs to \"%2$s\"</string>
<string name="updated_playlist_error">Failed to update \"%s\", please try later.</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 061cfae7..c4a53b99 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BasicButton">
- <item name="android:background">@drawable/abc_item_background_holo_light</item>
+ <item name="android:background">?attr/selectableItemBackground</item>
</style>
<style name="MoreButton" parent="BasicButton">