From fbd1a68042dfc3601eaa0a9e37b3957bbdd51420 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 22 Jul 2015 20:03:30 -0700 Subject: Update details dialogs, remove play/cache/delete from more menus to keep context menus smaller --- .../daneren2005/dsub/adapter/DetailsAdapter.java | 62 +++++++ .../daneren2005/dsub/adapter/SectionAdapter.java | 29 +++- .../daneren2005/dsub/fragments/MainFragment.java | 57 +++++-- .../dsub/fragments/SelectDirectoryFragment.java | 12 -- .../dsub/fragments/SelectPlaylistFragment.java | 60 ++++--- .../dsub/fragments/SelectPodcastsFragment.java | 30 +++- .../dsub/fragments/SubsonicFragment.java | 178 ++++++--------------- .../dsub/service/OfflineMusicService.java | 46 +++++- .../daneren2005/dsub/service/RESTMusicService.java | 2 +- .../github/daneren2005/dsub/util/FileUtil.java | 19 ++- .../java/github/daneren2005/dsub/util/Util.java | 32 ++++ app/src/main/res/layout/details_item.xml | 28 ++++ app/src/main/res/menu/select_album_context.xml | 40 +---- .../main/res/menu/select_album_context_offline.xml | 30 +--- app/src/main/res/menu/select_artist_context.xml | 40 +---- .../res/menu/select_artist_context_offline.xml | 30 ---- app/src/main/res/menu/select_bookmark_context.xml | 12 -- app/src/main/res/menu/select_playlist_context.xml | 15 -- .../res/menu/select_playlist_context_offline.xml | 14 +- .../res/menu/select_podcast_episode_context.xml | 12 +- .../select_podcast_episode_context_offline.xml | 8 +- app/src/main/res/values-de/strings.xml | 8 +- app/src/main/res/values-es/strings.xml | 8 +- app/src/main/res/values-fr/strings.xml | 6 - app/src/main/res/values-hu/strings.xml | 6 - app/src/main/res/values-ru/strings.xml | 6 - app/src/main/res/values/strings.xml | 44 ++++- 27 files changed, 399 insertions(+), 435 deletions(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java create mode 100644 app/src/main/res/layout/details_item.xml delete mode 100644 app/src/main/res/menu/select_artist_context_offline.xml (limited to 'app/src/main') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java new file mode 100644 index 00000000..efafe27a --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java @@ -0,0 +1,62 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.adapter; + +import android.content.Context; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.text.util.Linkify; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.List; + +import github.daneren2005.dsub.R; + +public class DetailsAdapter extends ArrayAdapter { + private List headers; + private List details; + + public DetailsAdapter(Context context, int layout, List headers, List details) { + super(context, layout, headers); + + this.headers = headers; + this.details = details; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent){ + View view; + if(convertView == null) { + view = LayoutInflater.from(getContext()).inflate(R.layout.details_item, null); + } else { + view = convertView; + } + + TextView nameView = (TextView) view.findViewById(R.id.detail_name); + TextView detailsView = (TextView) view.findViewById(R.id.detail_value); + + nameView.setText(headers.get(position)); + + detailsView.setText(details.get(position)); + Linkify.addLinks(detailsView, Linkify.ALL); + + return view; + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java index 9d577409..3f6086d5 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java @@ -178,9 +178,7 @@ public abstract class SectionAdapter extends RecyclerView.Adapter extends RecyclerView.Adapter extends RecyclerView.Adapter { } private void showAboutDialog() { - new LoadingTask(context) { + new LoadingTask(context) { + Long[] used; + long bytesTotalFs; + long bytesAvailableFs; + @Override - protected String doInBackground() throws Throwable { + protected Void doInBackground() throws Throwable { File rootFolder = FileUtil.getMusicDirectory(context); StatFs stat = new StatFs(rootFolder.getPath()); - long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize(); - long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize(); - - Pair used = FileUtil.getUsedSize(context, rootFolder); - - return getResources().getString(R.string.main_about_text, - context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName, - used.getFirst(), - Util.formatLocalizedBytes(used.getSecond(), context), - Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context), - Util.formatLocalizedBytes(bytesAvailableFs, context), - Util.formatLocalizedBytes(bytesTotalFs, context)); + bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize(); + bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize(); + + used = FileUtil.getUsedSize(context, rootFolder); + return null; } @Override - protected void done(String msg) { + protected void done(Void result) { + List headers = new ArrayList<>(); + List details = new ArrayList<>(); + + headers.add(R.string.details_author); + details.add("Scott Jackson"); + + headers.add(R.string.details_email); + details.add("dsub.android@gmail.com"); + try { - Util.info(context, R.string.main_about_title, msg); + headers.add(R.string.details_version); + details.add(context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName); } catch(Exception e) { - Util.toast(context, "Failed to open dialog"); + details.add(""); } + + Resources res = context.getResources(); + headers.add(R.string.details_files_cached); + details.add(Long.toString(used[0])); + + headers.add(R.string.details_files_permanent); + details.add(Long.toString(used[1])); + + headers.add(R.string.details_used_space); + details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(used[2], context), Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context))); + + headers.add(R.string.details_available_space); + details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(bytesAvailableFs, context), Util.formatLocalizedBytes(bytesTotalFs, context))); + + Util.showDetailsDialog(context, R.string.main_about_title, headers, details); } }.execute(); } 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 2ab8316c..555e308e 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -360,18 +360,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section } @Override public boolean onContextItemSelected(MenuItem menuItem, UpdateView updateView, Entry entry) { - if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) { - List songs = new ArrayList(); - songs.add(entry); - Iterator it = entries.listIterator(entries.indexOf(entry)); - while(it.hasNext()) { - songs.add((Entry) it.next()); - } - - playNow(songs); - return true; - } - if(onContextItemSelected(menuItem, entry)) { return true; } 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 5b7e68a6..b1587e24 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java @@ -68,40 +68,13 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment { @Override public boolean onContextItemSelected(MenuItem menuItem, UpdateView updateView, Playlist playlist) { - SubsonicFragment fragment; - Bundle args; - FragmentTransaction trans; switch (menuItem.getItemId()) { - case R.id.playlist_menu_download: - downloadPlaylist(playlist.getId(), playlist.getName(), false, true, false, false, true); - break; case R.id.playlist_menu_sync: syncPlaylist(playlist); break; case R.id.playlist_menu_stop_sync: stopSyncPlaylist(playlist); break; - case R.id.playlist_menu_play_now: - fragment = new SelectDirectoryFragment(); - args = new Bundle(); - args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId()); - args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName()); - args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true); - fragment.setArguments(args); - - replaceFragment(fragment); - break; - case R.id.playlist_menu_play_shuffled: - fragment = new SelectDirectoryFragment(); - args = new Bundle(); - args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId()); - args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName()); - args.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); - args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true); - fragment.setArguments(args); - - replaceFragment(fragment); - break; case R.id.playlist_menu_delete: deletePlaylist(playlist); break; @@ -213,12 +186,33 @@ public class SelectPlaylistFragment extends SelectRecyclerFragment { } private void displayPlaylistInfo(final Playlist playlist) { - String message = "Owner: " + playlist.getOwner() + "\nComments: " + - ((playlist.getComment() == null) ? "" : playlist.getComment()) + - "\nSong Count: " + playlist.getSongCount() + - ((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic())) + - "\nCreated: " + Util.formatDate(context, playlist.getCreated()); - Util.info(context, playlist.getName(), message); + List headers = new ArrayList<>(); + List details = new ArrayList<>(); + + if(playlist.getOwner() != null) { + headers.add(R.string.details_owner); + details.add(playlist.getOwner()); + } + + if(playlist.getComment() != null) { + headers.add(R.string.details_comments); + details.add(playlist.getComment()); + } + + headers.add(R.string.details_song_count); + details.add(playlist.getSongCount()); + + if(playlist.getPublic() != null) { + headers.add(R.string.details_public); + details.add(Boolean.toString(playlist.getPublic())); + } + + if(playlist.getCreated() != null) { + headers.add(R.string.details_created); + details.add(Util.formatDate(context, playlist.getCreated())); + } + + Util.showDetailsDialog(context, R.string.details_title_playlist, headers, details); } private void updatePlaylistInfo(final Playlist playlist) { 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 f8afce6e..381453c0 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java @@ -214,13 +214,29 @@ public class SelectPodcastsFragment extends SelectRecyclerFragment headers = new ArrayList<>(); + List details = new ArrayList<>(); + + if(channel.getName() != null) { + headers.add(R.string.details_title); + details.add(channel.getName()); + } + + headers.add(R.string.details_url); + details.add(channel.getUrl()); + headers.add(R.string.details_status); + details.add(channel.getStatus()); + + if(channel.getErrorMessage() != null) { + headers.add(R.string.details_error); + details.add(channel.getErrorMessage()); + } + if(channel.getDescription() != null) { + headers.add(R.string.details_description); + details.add(channel.getDescription()); + } + + Util.showDetailsDialog(context, R.string.details_title_podcast, headers, details); } private void deletePodcast(final PodcastChannel channel) { 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 438649c3..6925b2da 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java @@ -241,9 +241,8 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } else if(selected instanceof Artist) { Artist artist = (Artist) selected; if(Util.isOffline(context)) { - menuInflater.inflate(R.menu.select_artist_context_offline, menu); - } - else { + // menuInflater.inflate(R.menu.select_artist_context_offline, menu); + } else { menuInflater.inflate(R.menu.select_artist_context, menu); menu.findItem(R.id.artist_menu_star).setTitle(artist.isStarred() ? R.string.common_unstar : R.string.common_star); @@ -281,56 +280,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true)) { menu.setGroupVisible(R.id.hide_rating, false); } - - if(!Util.isOffline(context)) { - // If we are looking at a standard song view, get downloadFile to cache what options to show - if(updateView instanceof SongView) { - SongView songView = (SongView) updateView; - DownloadFile downloadFile = songView.getDownloadFile(); - - try { - if(downloadFile != null) { - if(downloadFile.isWorkDone()) { - // Remove permanent cache menu if already perma cached - if(downloadFile.isSaved()) { - menu.removeItem(R.id.song_menu_pin); - } - - // Remove cache option no matter what if already downloaded - menu.removeItem(R.id.song_menu_download); - } else { - // Remove delete option if nothing to delete - menu.removeItem(R.id.song_menu_delete); - } - } - } catch(Exception e) { - Log.w(TAG, "Failed to lookup downloadFile info", e); - } - } - // Apply similar logic to album views - else if(updateView instanceof AlbumView || updateView instanceof ArtistView || updateView instanceof ArtistEntryView) { - File folder = null; - int id = 0; - if(updateView instanceof AlbumView) { - folder = ((AlbumView) updateView).getFile(); - id = R.id.album_menu_delete; - } else if(updateView instanceof ArtistView) { - folder = ((ArtistView) updateView).getFile(); - id = R.id.artist_menu_delete; - } else if(updateView instanceof ArtistEntryView) { - folder = ((ArtistEntryView) updateView).getFile(); - id = R.id.artist_menu_delete; - } - - try { - if(folder != null && !folder.exists()) { - menu.removeItem(id); - } - } catch(Exception e) { - Log.w(TAG, "Failed to lookup album directory info", e); - } - } - } } protected void recreateContextMenu(Menu menu) { @@ -355,60 +304,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR songs.add(entry); switch (menuItem.getItemId()) { - case R.id.artist_menu_play_now: - downloadRecursively(artist.getId(), false, false, true, false, false); - break; - case R.id.artist_menu_play_shuffled: - downloadRecursively(artist.getId(), false, false, true, true, false); - break; - case R.id.artist_menu_play_next: - downloadRecursively(artist.getId(), false, true, false, false, false, true); - break; - case R.id.artist_menu_play_last: - downloadRecursively(artist.getId(), false, true, false, false, false); - break; - case R.id.artist_menu_download: - downloadRecursively(artist.getId(), false, true, false, false, true); - break; - case R.id.artist_menu_pin: - downloadRecursively(artist.getId(), true, true, false, false, true); - break; - case R.id.artist_menu_delete: - deleteRecursively(artist); - break; case R.id.artist_menu_star: toggleStarred(artist); break; - case R.id.album_menu_play_now: - artistOverride = true; - downloadRecursively(entry.getId(), false, false, true, false, false); - break; - case R.id.album_menu_play_shuffled: - artistOverride = true; - downloadRecursively(entry.getId(), false, false, true, true, false); - break; - case R.id.album_menu_play_next: - artistOverride = true; - downloadRecursively(entry.getId(), false, true, false, false, false, true); - break; - case R.id.album_menu_play_last: - artistOverride = true; - downloadRecursively(entry.getId(), false, true, false, false, false); - break; - case R.id.album_menu_download: - artistOverride = true; - downloadRecursively(entry.getId(), false, true, false, false, true); - break; - case R.id.album_menu_pin: - artistOverride = true; - downloadRecursively(entry.getId(), true, true, false, false, true); - break; case R.id.album_menu_star: toggleStarred(entry); break; - case R.id.album_menu_delete: - deleteRecursively(entry); - break; case R.id.album_menu_info: displaySongInfo(entry); break; @@ -418,21 +319,9 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR case R.id.album_menu_share: createShare(songs); break; - case R.id.song_menu_play_now: - playNow(songs); - break; - case R.id.song_menu_play_next: - getDownloadService().download(songs, false, false, true, false); - break; - case R.id.song_menu_play_last: - getDownloadService().download(songs, false, false, false, false); - break; case R.id.song_menu_download: getDownloadService().downloadBackground(songs, false); break; - case R.id.song_menu_pin: - getDownloadService().downloadBackground(songs, true); - break; case R.id.song_menu_delete: getDownloadService().delete(songs); break; @@ -1262,7 +1151,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR }.execute(); } - @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) public void displaySongInfo(final Entry song) { Integer duration = null; Integer bitrate = null; @@ -1302,55 +1190,83 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } } - String msg = ""; + List headers = new ArrayList<>(); + List details = new ArrayList<>(); if(song instanceof PodcastEpisode) { - msg += "Podcast: " + song.getArtist() + "\nStatus: " + ((PodcastEpisode)song).getStatus(); + headers.add(R.string.details_podcast); + details.add(song.getArtist()); + + headers.add(R.string.details_status); + details.add(((PodcastEpisode)song).getStatus()); } else if(!song.isVideo()) { if(song.getArtist() != null && !"".equals(song.getArtist())) { - msg += "Artist: " + song.getArtist(); + headers.add(R.string.details_artist); + details.add(song.getArtist()); } if(song.getAlbum() != null && !"".equals(song.getAlbum())) { - msg += "\nAlbum: " + song.getAlbum(); + headers.add(R.string.details_album); + details.add(song.getAlbum()); } } if(song.getTrack() != null && song.getTrack() != 0) { - msg += "\nTrack: " + song.getTrack(); + headers.add(R.string.details_track); + details.add(Integer.toString(song.getTrack())); } if(song.getGenre() != null && !"".equals(song.getGenre())) { - msg += "\nGenre: " + song.getGenre(); + headers.add(R.string.details_genre); + details.add(song.getGenre()); } if(song.getYear() != null && song.getYear() != 0) { - msg += "\nYear: " + song.getYear(); + headers.add(R.string.details_year); + details.add(Integer.toString(song.getYear())); } if(!Util.isOffline(context) && song.getSuffix() != null) { - msg += "\nServer Format: " + song.getSuffix(); + headers.add(R.string.details_server_format); + details.add(song.getSuffix()); + if(song.getBitRate() != null && song.getBitRate() != 0) { - msg += "\nServer Bitrate: " + song.getBitRate() + " kbps"; + headers.add(R.string.details_server_bitrate); + details.add(song.getBitRate() + " kbps"); } } if(format != null && !"".equals(format)) { - msg += "\nCached Format: " + format; + headers.add(R.string.details_cached_format); + details.add(format); } if(bitrate != null && bitrate != 0) { - msg += "\nCached Bitrate: " + bitrate + " kbps"; + headers.add(R.string.details_cached_bitrate); + details.add(bitrate + " kbps"); } if(size != 0) { - msg += "\nSize: " + Util.formatLocalizedBytes(size, context); + headers.add(R.string.details_size); + details.add(Util.formatLocalizedBytes(size, context)); } if(song.getDuration() != null && song.getDuration() != 0) { - msg += "\nLength: " + Util.formatDuration(song.getDuration()); + headers.add(R.string.details_length); + details.add(Util.formatDuration(song.getDuration())); } if(song.getBookmark() != null) { - msg += "\nBookmark Position: " + Util.formatDuration(song.getBookmark().getPosition() / 1000); + headers.add(R.string.details_bookmark_position); + details.add(Util.formatDuration(song.getBookmark().getPosition() / 1000)); } if(song.getRating() != 0) { - msg += "\nRating: " + song.getRating() + " stars"; + headers.add(R.string.details_rating); + details.add(song.getRating() + " stars"); } if(song instanceof PodcastEpisode) { - msg += "\n\nDescription: " + song.getAlbum(); + headers.add(R.string.details_description); + details.add(song.getAlbum()); } - Util.info(context, song.getTitle(), msg); + int title; + if(song.isDirectory()) { + title = R.string.details_title_album; + } else if(song instanceof PodcastEpisode) { + title = R.string.details_title_podcast; + } else { + title = R.string.details_title_song; + } + Util.showDetailsDialog(context, title, headers, details); } protected void playVideo(Entry entry) { diff --git a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java index b4105d07..8ebf4c13 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java @@ -382,6 +382,42 @@ public class OfflineMusicService implements MusicService { String id = file.getName(); String filename = server + ": " + FileUtil.getBaseName(id); Playlist playlist = new Playlist(server, filename); + + Reader reader = null; + BufferedReader buffer = null; + try { + int songCount = 0; + reader = new FileReader(file); + buffer = new BufferedReader(reader); + + String line = buffer.readLine(); + while( (line = buffer.readLine()) != null ){ + // No matter what, end file can't have .complete in it + line = line.replace(".complete", ""); + File entryFile = new File(line); + + // Don't add file to playlist if it doesn't exist as cached or pinned! + File checkFile = entryFile; + if(!checkFile.exists()) { + // If normal file doens't exist, check if .complete version does + checkFile = new File(entryFile.getParent(), FileUtil.getBaseName(entryFile.getName()) + + ".complete." + FileUtil.getExtension(entryFile.getName())); + } + + String entryName = getName(entryFile); + if(checkFile.exists() && entryName != null){ + songCount++; + } + } + + playlist.setSongCount(Integer.toString(songCount)); + } catch(Exception e) { + Log.w(TAG, "Failed to count songs in playlist", e); + } finally { + Util.close(buffer); + Util.close(reader); + } + playlists.add(playlist); } } @@ -684,10 +720,16 @@ public class OfflineMusicService implements MusicService { for(File file: dir.listFiles()) { BufferedReader br = new BufferedReader(new FileReader(file)); while ((line = br.readLine()) != null && !"".equals(line)) { + String[] parts = line.split("\t"); + PodcastChannel channel = new PodcastChannel(); - channel.setId(line); - channel.setName(line); + channel.setId(parts[0]); + channel.setName(parts[0]); channel.setStatus("completed"); + + if(parts.length > 1) { + channel.setUrl(parts[1]); + } if(FileUtil.getPodcastDirectory(context, channel).exists() && !channels.contains(channel)) { channels.add(channel); diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java index 459c8c9e..ed34e1cb 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java @@ -1204,7 +1204,7 @@ public class RESTMusicService implements MusicService { String content = ""; for(PodcastChannel channel: channels) { - content += channel.getName() + "\n"; + content += channel.getName() + "\t" + channel.getUrl() + "\n"; } File file = FileUtil.getPodcastFile(context, Util.getServerName(context, getInstance(context))); diff --git a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java index 990eae06..185a6b29 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java +++ b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java @@ -741,23 +741,28 @@ public class FileUtil { return index == -1 ? name : name.substring(0, index); } - public static Pair getUsedSize(Context context, File file) { + public static Long[] getUsedSize(Context context, File file) { long number = 0L; + long permanent = 0L; long size = 0L; if(file.isFile()) { if(isMediaFile(file)) { - return new Pair(1L, file.length()); + if(file.getAbsolutePath().indexOf(".complete") == -1) { + permanent++; + } + return new Long[] {1L, permanent, file.length()}; } else { - return new Pair(0L, 0L); + return new Long[] {0L, 0L, 0L}; } } else { for (File child : FileUtil.listFiles(file)) { - Pair pair = getUsedSize(context, child); - number += pair.getFirst(); - size += pair.getSecond(); + Long[] pair = getUsedSize(context, child); + number += pair[0]; + permanent += pair[1]; + size += pair[2]; } - return new Pair(number, size); + return new Long[] {number, permanent, size}; } } diff --git a/app/src/main/java/github/daneren2005/dsub/util/Util.java b/app/src/main/java/github/daneren2005/dsub/util/Util.java index ce26bf1e..db0cb7c1 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/Util.java +++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java @@ -19,6 +19,7 @@ package github.daneren2005.dsub.util; import android.annotation.TargetApi; import android.app.Activity; +import android.support.annotation.StringRes; import android.support.v7.app.AlertDialog; import android.content.ComponentName; import android.content.Context; @@ -45,11 +46,14 @@ import android.text.method.LinkMovementMethod; import android.text.util.Linkify; import android.util.Log; import android.view.Gravity; +import android.widget.ArrayAdapter; +import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import github.daneren2005.dsub.R; import github.daneren2005.dsub.activity.SettingsActivity; import github.daneren2005.dsub.activity.SubsonicFragmentActivity; +import github.daneren2005.dsub.adapter.DetailsAdapter; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.PlayerState; import github.daneren2005.dsub.domain.RepeatMode; @@ -73,8 +77,10 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.TimeZone; @@ -1137,6 +1143,32 @@ public final class Util { ((TextView)dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); } + public static void showDetailsDialog(Context context, @StringRes int title, List headers, List details) { + List headerStrings = new ArrayList<>(); + for(@StringRes Integer res: headers) { + headerStrings.add(context.getResources().getString(res)); + } + showDetailsDialog(context, context.getResources().getString(title), headerStrings, details); + } + public static void showDetailsDialog(Context context, String title, List headers, List details) { + ListView listView = new ListView(context); + listView.setAdapter(new DetailsAdapter(context, R.layout.details_item, headers, details)); + listView.setDivider(null); + listView.setScrollbarFadingEnabled(false); + + new AlertDialog.Builder(context) + // .setIcon(android.R.drawable.ic_dialog_info) + .setTitle(title) + .setView(listView) + .setPositiveButton(R.string.common_close, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int i) { + dialog.dismiss(); + } + }) + .show(); + } + public static void sleepQuietly(long millis) { try { Thread.sleep(millis); diff --git a/app/src/main/res/layout/details_item.xml b/app/src/main/res/layout/details_item.xml new file mode 100644 index 00000000..4ef5fef0 --- /dev/null +++ b/app/src/main/res/layout/details_item.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/select_album_context.xml b/app/src/main/res/menu/select_album_context.xml index 5b2529e7..388fd1f5 100644 --- a/app/src/main/res/menu/select_album_context.xml +++ b/app/src/main/res/menu/select_album_context.xml @@ -1,48 +1,10 @@ - - + - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/menu/select_album_context_offline.xml b/app/src/main/res/menu/select_album_context_offline.xml index a1805f5b..60858d91 100644 --- a/app/src/main/res/menu/select_album_context_offline.xml +++ b/app/src/main/res/menu/select_album_context_offline.xml @@ -1,31 +1,9 @@ - - - - - - - - + - - - - - + - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/menu/select_bookmark_context.xml b/app/src/main/res/menu/select_bookmark_context.xml index 2b1b83fd..d52db105 100644 --- a/app/src/main/res/menu/select_bookmark_context.xml +++ b/app/src/main/res/menu/select_bookmark_context.xml @@ -14,18 +14,6 @@ android:id="@+id/song_menu_show_artist" android:title="@string/menu.show_artist"/> - - - - - - diff --git a/app/src/main/res/menu/select_playlist_context.xml b/app/src/main/res/menu/select_playlist_context.xml index 47033d9c..4941e94b 100644 --- a/app/src/main/res/menu/select_playlist_context.xml +++ b/app/src/main/res/menu/select_playlist_context.xml @@ -7,21 +7,6 @@ android:title="@string/common.info" /> - - - - - - diff --git a/app/src/main/res/menu/select_playlist_context_offline.xml b/app/src/main/res/menu/select_playlist_context_offline.xml index d63aec17..6745d850 100644 --- a/app/src/main/res/menu/select_playlist_context_offline.xml +++ b/app/src/main/res/menu/select_playlist_context_offline.xml @@ -1,13 +1,7 @@ - - + - - \ No newline at end of file diff --git a/app/src/main/res/menu/select_podcast_episode_context.xml b/app/src/main/res/menu/select_podcast_episode_context.xml index bacccda3..31f2a051 100644 --- a/app/src/main/res/menu/select_podcast_episode_context.xml +++ b/app/src/main/res/menu/select_podcast_episode_context.xml @@ -7,7 +7,7 @@ android:title="@string/common.info" /> - @@ -23,16 +23,16 @@ android:id="@+id/song_menu_play_last" android:title="@string/common.play_last"/> - - - - - + - - @@ -23,8 +23,8 @@ android:id="@+id/song_menu_play_last" android:title="@string/common.play_last"/> - - diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index cd387b65..d4a239a6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -42,13 +42,7 @@ Willkommen zu DSub! Die App ist aktuell für den Subsonic-Demo-Server konfiguriert. Nachdem Sie Ihren eigenen Server aufgesetzt haben (verfügbar unter subsonic.org) könne Sie diesen unter Einstellungen konfigurieren. Über DSub - Autor: Scott Jackson - \nEmail: dsub.android@gmail.com - \nVersion: %1$s - \nLokal gespeicherte Titel: %2$s - \nGenutzter Speicher: %3$s von %4$s - \nVerfügbarer Speicher: %5$s von %6$s - FAQ + FAQ Cache vs Permanenter Cache: diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3c2593e2..e4d80211 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -41,13 +41,7 @@ Bienvenido! Bienvenido a DSub! Ahora la aplicación está configurada para usar el servidor de demostración de Subsonic. Cuando configures tu servidor personal (disponible en subsonic.org), accede a Preferencias y cambia la configuración para conectarte. Acerca de DSub - Autor: Scott Jackson - \nEmail: dsub.android@gmail.com - \nVersión: %1$s - \nArchivos en caché: %2$s - \nEspacio usado: %3$s of %4$s - \nEspacio disponible: %5$s of %6$s - Seleccionar servidor + Seleccionar servidor Reproducción aleatoria Modo Offline Modo Online diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index cea3c77d..84e3dd75 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -41,12 +41,6 @@ Bienvenue ! Bienvenue dans DSub ! L\'application est actuellement configurée pour se connecter au serveur de démo Subsonic (demo.subsonic.org). Vous pouvez configurer votre propre serveur dans les paramètres. Choisir Paramètres et mettre à jour la configuration pour vous y connecter. A propos de DSub - Auteur : Scott Jackson - \nEmail : dsub.android@gmail.com - \nVersion : %1$s - \nFichiers en cache : %2$s - \nEspace utilisé : %3$s de %4$s - \nEspace dispo. : %5$s de %6$s FAQ Üdvözli a DSub! Az alkalmazás még nincs beállítva. Miután konfigurálta saját kiszolgálóját (elérhető: subsonic.org), húzza balról jobbra az oldalsávot, lépjen be a Beállítások menüpontba és adja meg a kapcsolódási adatokat! DSub információk - Fejlesztő: Scott Jackson - \nEmail: dsub.android@gmail.com - \nVerzió: %1$s - \nGyorsítótárazott fájlok: %2$s - \nFelhasznált tároló: %3$s/%4$s - \nFelhasználható tároló: %5$s/%6$s GYIK Добро пожаловать в DSub! Это приложение настроено на работу с демо сервером Subsonic. После настройки Вашего персонального сервера (доступен на subsonic.org), пожалуйста, перейдите в Настройки и измените параметры для подключения. О программе DSub - Автор: Scott Jackson - \nEmail: dsub.android@gmail.com - \nВерсия: %1$s - \nFiles Cached: %2$s - \nИспользовано места: %3$s из %4$s - \nДоступно места: %5$s из %6$s Выбрать сервер Случайное воспроизведение Отключиться diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f7c91da3..74d69e5e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,6 +25,7 @@ cache No data Warning + Close Home Library @@ -42,12 +43,6 @@ Welcome to DSub! The app is currently configured to use the Subsonic demo server. After you\'ve set up your personal server (available from subsonic.org), please go to Settings and change the configuration to connect to it. About DSub - Author: Scott Jackson - \nEmail: dsub.android@gmail.com - \nVersion: %1$s - \nFiles Cached: %2$s - \nUsed Space: %3$s of %4$s - \nAvailable Space: %5$s of %6$s FAQ Toggle offline: Do Nothing + Song Details + Album Details + Podcast Details + Playlist Details + Podcast + Status + Artist + Album + Track + Genre + Year + Server Format + Server Bitrate + Cached Format + Cached Bitrate + Size + Length + Bookmark Position + Rating + Description + Owner + Comments + Song Count + Public + Created + Title + URL + Error Message + Author + Email + Version + Files Cached + Permanent Cached + Used Space + Available Space + %1$s of %2$s + No songs One song -- cgit v1.2.3