aboutsummaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2016-10-22 14:37:14 -0700
committerScott Jackson <daneren2005@gmail.com>2016-10-22 14:37:14 -0700
commit61979e9893ce4bd5e4fdee1fc3349a04ccc7a8a7 (patch)
treed23db8f707e67507f2b43359544f9f635a816f37 /app/src/main
parent2b973ff6571e565bb6752be616439dbe26b2ce88 (diff)
parent2b4eb81c25e6170e881c8074672d4f2ae0226f4f (diff)
downloaddsub-61979e9893ce4bd5e4fdee1fc3349a04ccc7a8a7.tar.gz
dsub-61979e9893ce4bd5e4fdee1fc3349a04ccc7a8a7.tar.bz2
dsub-61979e9893ce4bd5e4fdee1fc3349a04ccc7a8a7.zip
Merge branch 'edge'
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml3
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java14
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java16
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java85
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java12
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java12
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java30
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java26
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java58
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DLNAController.java54
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java28
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/MusicService.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java845
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RemoteController.java67
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java553
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java65
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java44
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java57
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Util.java16
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java13
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java14
-rw-r--r--app/src/main/res/layout-land/download.xml2
-rw-r--r--app/src/main/res/layout/abstract_fragment_activity.xml2
-rw-r--r--app/src/main/res/layout/change_email.xml2
-rw-r--r--app/src/main/res/layout/confirm_password.xml2
-rw-r--r--app/src/main/res/layout/create_bookmark.xml2
-rw-r--r--app/src/main/res/layout/create_podcast.xml2
-rw-r--r--app/src/main/res/layout/create_user.xml6
-rw-r--r--app/src/main/res/layout/notification.xml1
-rw-r--r--app/src/main/res/layout/notification_expanded.xml1
-rw-r--r--app/src/main/res/layout/select_album_header.xml2
-rw-r--r--app/src/main/res/layout/shuffle_dialog.xml6
-rw-r--r--app/src/main/res/layout/update_playlist.xml6
-rw-r--r--app/src/main/res/layout/update_share.xml6
-rw-r--r--app/src/main/res/values/strings.xml2
-rw-r--r--app/src/main/res/xml/changelog.xml10
46 files changed, 640 insertions, 1458 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 65305cbd..37643a21 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,12 +23,15 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<uses-feature android:name="android.hardware.wifi" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false"/>
+ <uses-feature android:name="android.hardware.location" android:required="false"/>
+ <uses-feature android:name="android.hardware.location.network" android:required="false"/>
<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/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index 9b14f4f6..3b533fae 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -95,7 +95,8 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
protected static boolean actionbarColored;
private static final int MENU_GROUP_SERVER = 10;
private static final int MENU_ITEM_SERVER_BASE = 100;
- private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
+ public static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
+ public static final int PERMISSIONS_REQUEST_LOCATION = 2;
private final List<Runnable> afterServiceAvailable = new ArrayList<>();
private boolean drawerIdle = true;
@@ -245,7 +246,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
if (theme != null && !theme.equals(ThemeUtil.getTheme(this)) || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false) || actionbarColored != prefs.getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
restart();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- DrawableTint.wipeTintCache();
+ DrawableTint.clearCache();
}
populateTabs();
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 c7190046..fb8221c8 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -28,8 +28,8 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Bundle;
-import android.os.Handler;
import android.preference.PreferenceManager;
+import android.provider.MediaStore;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
@@ -37,8 +37,6 @@ import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -48,9 +46,6 @@ import com.sothree.slidinguppanel.SlidingUpPanelLayout;
import java.io.File;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -77,6 +72,7 @@ import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.updates.Updater;
import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.UserUtil;
@@ -138,6 +134,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
stopService(new Intent(this, DownloadService.class));
finish();
getImageLoader().clearCache();
+ DrawableTint.clearCache();
} else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Download");
lastSelectedPosition = R.id.drawer_downloading;
@@ -402,9 +399,12 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
if(currentFragment instanceof SearchFragment) {
String query = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY);
boolean autoplay = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
+ String artist = intent.getStringExtra(MediaStore.EXTRA_MEDIA_ARTIST);
+ String album = intent.getStringExtra(MediaStore.EXTRA_MEDIA_ALBUM);
+ String title = intent.getStringExtra(MediaStore.EXTRA_MEDIA_TITLE);
if (query != null) {
- ((SearchFragment)currentFragment).search(query, autoplay);
+ ((SearchFragment)currentFragment).search(query, autoplay, artist, album, title);
}
getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_QUERY);
} else {
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
index c0effe27..641b118f 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
@@ -55,6 +55,22 @@ public class VoiceQueryReceiverActivity extends Activity {
if(!GMS_SEARCH_ACTION.equals(getIntent().getAction())) {
intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
}
+
+ String artist = getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_ARTIST);
+ if(artist != null) {
+ intent.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
+ }
+
+ String album = getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_ALBUM);
+ if(album != null) {
+ intent.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
+ }
+
+ String title = getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_TITLE);
+ if(title != null) {
+ intent.putExtra(MediaStore.EXTRA_MEDIA_TITLE, title);
+ }
+
intent.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_FOCUS));
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Util.startActivityWithoutTransition(VoiceQueryReceiverActivity.this, intent);
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 eed714af..dfff45cd 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
@@ -3,7 +3,10 @@ package github.daneren2005.dsub.fragments;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import android.content.Intent;
import android.os.Bundle;
@@ -26,6 +29,7 @@ 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.MusicDirectory.Entry;
import github.daneren2005.dsub.domain.SearchCritera;
import github.daneren2005.dsub.domain.SearchResult;
import github.daneren2005.dsub.service.MusicService;
@@ -132,7 +136,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Serializable> updateView, Serializable item) {
onCreateContextMenuSupport(menu, menuInflater, updateView, item);
- if(item instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) item).isVideo() && !Util.isOffline(context)) {
+ if(item instanceof Entry && !((Entry) item).isVideo() && !Util.isOffline(context)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
recreateContextMenu(menu);
@@ -152,8 +156,8 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
public void onItemClicked(UpdateView<Serializable> updateView, Serializable item) {
if (item instanceof Artist) {
onArtistSelected((Artist) item, false);
- } else if (item instanceof MusicDirectory.Entry) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
+ } else if (item instanceof Entry) {
+ Entry entry = (Entry) item;
if (entry.isDirectory()) {
onAlbumSelected(entry, false);
} else if (entry.isVideo()) {
@@ -165,12 +169,12 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
}
@Override
- protected List<MusicDirectory.Entry> getSelectedEntries() {
+ protected List<Entry> getSelectedEntries() {
List<Serializable> selected = adapter.getSelected();
- List<MusicDirectory.Entry> selectedMedia = new ArrayList<>();
+ List<Entry> selectedMedia = new ArrayList<>();
for(Serializable ser: selected) {
- if(ser instanceof MusicDirectory.Entry) {
- selectedMedia.add((MusicDirectory.Entry) ser);
+ if(ser instanceof Entry) {
+ selectedMedia.add((Entry) ser);
}
}
@@ -182,7 +186,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
return true;
}
- public void search(final String query, final boolean autoplay) {
+ public void search(final String query, final boolean autoplay, final String artist, final String album, final String title) {
if(skipSearch) {
skipSearch = false;
return;
@@ -202,7 +206,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
searchResult = result;
recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, SearchFragment.this));
if (autoplay) {
- autoplay(query);
+ autoplay(query, artist, album, title);
}
}
@@ -232,7 +236,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
replaceFragment(fragment);
}
- private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) {
+ private void onAlbumSelected(Entry album, boolean autoplay) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, album.getId());
@@ -245,7 +249,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
replaceFragment(fragment);
}
- private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) {
+ private void onSongSelected(Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) {
DownloadService downloadService = getDownloadService();
if (downloadService != null) {
if (!append) {
@@ -260,7 +264,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
}
}
- private void onVideoSelected(MusicDirectory.Entry entry) {
+ private void onVideoSelected(Entry entry) {
int maxBitrate = Util.getMaxVideoBitrate(context);
Intent intent = new Intent(Intent.ACTION_VIEW);
@@ -268,6 +272,55 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
startActivity(intent);
}
+ private void autoplay(String query, String artistQuery, String albumQuery, String titleQuery) {
+ Log.i(TAG, "Query: '" + query + "' ( Artist: '" + artistQuery + "', Album: '" + albumQuery + "', Title: '" + titleQuery + "')");
+
+ if(titleQuery != null && !searchResult.getSongs().isEmpty()) {
+ titleQuery = titleQuery.toLowerCase();
+
+ TreeMap<Integer, Entry> tree = new TreeMap<>();
+ for(Entry song: searchResult.getSongs()) {
+ tree.put(Util.getStringDistance(song.getTitle().toLowerCase(), titleQuery), song);
+ }
+
+ Map.Entry<Integer, Entry> entry = tree.firstEntry();
+ if(entry.getKey() <= MIN_CLOSENESS) {
+ onSongSelected(entry.getValue(), false, false, true, false);
+ } else {
+ autoplay(query);
+ }
+ } else if(albumQuery != null && !searchResult.getAlbums().isEmpty()) {
+ albumQuery = albumQuery.toLowerCase();
+
+ TreeMap<Integer, Entry> tree = new TreeMap<>();
+ for(Entry album: searchResult.getAlbums()) {
+ tree.put(Util.getStringDistance(album.getTitle().toLowerCase(), albumQuery), album);
+ }
+
+ Map.Entry<Integer, Entry> entry = tree.firstEntry();
+ if(entry.getKey() <= MIN_CLOSENESS) {
+ onAlbumSelected(entry.getValue(), true);
+ } else {
+ autoplay(query);
+ }
+ } else if(artistQuery != null && !searchResult.getArtists().isEmpty()) {
+ artistQuery = artistQuery.toLowerCase();
+
+ TreeMap<Integer, Artist> tree = new TreeMap<>();
+ for(Artist artist: searchResult.getArtists()) {
+ tree.put(Util.getStringDistance(artist.getName().toLowerCase(), artistQuery), artist);
+ }
+ Map.Entry<Integer, Artist> entry = tree.firstEntry();
+ if(entry.getKey() <= MIN_CLOSENESS) {
+ onArtistSelected(entry.getValue(), true);
+ } else {
+ autoplay(query);
+ }
+ } else {
+ autoplay(query);
+ }
+ }
+
private void autoplay(String query) {
query = query.toLowerCase();
@@ -276,12 +329,12 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
artist = searchResult.getArtists().get(0);
artist.setCloseness(Util.getStringDistance(artist.getName().toLowerCase(), query));
}
- MusicDirectory.Entry album = null;
+ Entry album = null;
if(!searchResult.getAlbums().isEmpty()) {
album = searchResult.getAlbums().get(0);
album.setCloseness(Util.getStringDistance(album.getTitle().toLowerCase(), query));
}
- MusicDirectory.Entry song = null;
+ Entry song = null;
if(!searchResult.getSongs().isEmpty()) {
song = searchResult.getSongs().get(0);
song.setCloseness(Util.getStringDistance(song.getTitle().toLowerCase(), query));
@@ -289,10 +342,10 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
if(artist != null && (artist.getCloseness() <= MIN_CLOSENESS ||
(album == null || artist.getCloseness() <= album.getCloseness()) &&
- (song == null || artist.getCloseness() <= song.getCloseness()))) {
+ (song == null || artist.getCloseness() <= song.getCloseness()))) {
onArtistSelected(artist, true);
} else if(album != null && (album.getCloseness() <= MIN_CLOSENESS ||
- song == null || album.getCloseness() <= song.getCloseness())) {
+ song == null || album.getCloseness() <= song.getCloseness())) {
onAlbumSelected(album, true);
} else if(song != null) {
onSongSelected(song, false, false, true, false);
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 0ac968b7..d3a0bfe8 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -459,8 +459,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
}
List<Entry> songs = new ArrayList<Entry>();
getSongsRecursively(root, songs);
- root.replaceChildren(songs);
- return root;
+
+ // CachedMusicService is refreshing this data in the background, so will wipe out the songs list from root
+ MusicDirectory clonedRoot = new MusicDirectory(songs);
+ clonedRoot.setId(root.getId());
+ clonedRoot.setName(root.getName());
+ return clonedRoot;
}
private void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
@@ -733,7 +737,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
if(!artist) {
entryGridAdapter.setShowArtist(true);
}
- if(topTracks) {
+ if(topTracks || showAll) {
entryGridAdapter.setShowAlbum(true);
}
@@ -916,7 +920,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section
for(Integer index: indexes) {
entryGridAdapter.removeAt(index);
}
- Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
+ Util.toast(context, context.getResources().getString(R.string.removed_playlist, String.valueOf(indexes.size()), name));
}
@Override
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java
index c39e9f61..74c4b269 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java
@@ -133,7 +133,7 @@ public class SelectInternetRadioStationFragment extends SelectRecyclerFragment<I
connection.disconnect();
}
} catch (Exception e) {
- Log.e(TAG, "Failed to get stream data from playlist");
+ Log.e(TAG, "Failed to get stream data from playlist", e);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
index d5ba25f5..584a205a 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
@@ -15,12 +15,14 @@
package github.daneren2005.dsub.fragments;
+import android.Manifest;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
@@ -29,6 +31,8 @@ import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
@@ -47,6 +51,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicActivity;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.HeadphoneListenerService;
import github.daneren2005.dsub.service.MusicService;
@@ -188,6 +193,13 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
} else {
context.stopService(serviceIntent);
}
+ } else if(Constants.PREFERENCES_KEY_THEME.equals(key)) {
+ String value = sharedPreferences.getString(key, null);
+ if("day/night".equals(value) || "day/black".equals(value)) {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(context, new String[]{ Manifest.permission.ACCESS_COARSE_LOCATION }, SubsonicActivity.PERMISSIONS_REQUEST_LOCATION);
+ }
+ }
}
scheduleBackup();
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 818324ed..de230309 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -660,7 +660,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
});
- refreshLayout.setColorScheme(
+ refreshLayout.setColorSchemeResources(
R.color.holo_blue_light,
R.color.holo_orange_light,
R.color.holo_green_light,
@@ -683,7 +683,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
});
- refreshLayout.setColorScheme(
+ refreshLayout.setColorSchemeResources(
R.color.holo_blue_light,
R.color.holo_orange_light,
R.color.holo_green_light,
@@ -1113,7 +1113,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.updated_playlist, songs.size(), playlist.getName()));
+ Util.toast(context, context.getResources().getString(R.string.updated_playlist, String.valueOf(songs.size()), playlist.getName()));
}
@Override
@@ -1135,16 +1135,24 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
final EditText playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name);
final CheckBox overwriteCheckBox = (CheckBox) layout.findViewById(R.id.save_playlist_overwrite);
if(getSuggestion) {
- String playlistName = (getDownloadService() != null) ? getDownloadService().getSuggestedPlaylistName() : null;
+ DownloadService downloadService = getDownloadService();
+ String playlistName = null;
+ String playlistId = null;
+ if(downloadService != null) {
+ playlistName = downloadService.getSuggestedPlaylistName();
+ playlistId = downloadService.getSuggestedPlaylistId();
+ }
if (playlistName != null) {
playlistNameView.setText(playlistName);
- try {
- if(ServerInfo.checkServerVersion(context, "1.8.0") && Integer.parseInt(getDownloadService().getSuggestedPlaylistId()) != -1) {
- overwriteCheckBox.setChecked(true);
- overwriteCheckBox.setVisibility(View.VISIBLE);
+ if(playlistId != null) {
+ try {
+ if (ServerInfo.checkServerVersion(context, "1.8.0") && Integer.parseInt(playlistId) != -1) {
+ overwriteCheckBox.setChecked(true);
+ overwriteCheckBox.setVisibility(View.VISIBLE);
+ }
+ } catch (Exception e) {
+ Log.i(TAG, "Playlist id isn't a integer, probably MusicCabinet", e);
}
- } catch(Exception e) {
- Log.i(TAG, "Playlist id isn't a integer, probably MusicCabinet");
}
} else {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@@ -1205,6 +1213,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
protected void error(Throwable error) {
String msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
+ Log.e(TAG, "Failed to create playlist", error);
Util.toast(context, msg);
}
}.execute();
@@ -1234,6 +1243,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
}
+ Log.e(TAG, "Failed to overwrite playlist", error);
Util.toast(context, msg, false);
}
}.execute();
diff --git a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
index 00fb4624..9fd26fe5 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
@@ -20,6 +20,7 @@ package github.daneren2005.dsub.service;
import java.io.File;
import java.io.IOException;
+import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -27,8 +28,6 @@ import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.TimeUnit;
-import org.apache.http.HttpResponse;
-
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
@@ -733,11 +732,16 @@ public class CachedMusicService implements MusicService {
@Override
public Bitmap getCoverArt(Context context, Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
- return musicService.getCoverArt(context, entry, size, progressListener, task);
+ Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size);
+ if (bitmap != null) {
+ return bitmap;
+ } else {
+ return musicService.getCoverArt(context, entry, size, progressListener, task);
+ }
}
@Override
- public HttpResponse getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
+ public HttpURLConnection getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task);
}
@@ -1157,7 +1161,12 @@ public class CachedMusicService implements MusicService {
@Override
public Bitmap getAvatar(String username, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
- return musicService.getAvatar(username, size, context, progressListener, task);
+ Bitmap bitmap = FileUtil.getAvatarBitmap(context, username, size);
+ if(bitmap != null) {
+ return bitmap;
+ } else {
+ return musicService.getAvatar(username, size, context, progressListener, task);
+ }
}
@Override
@@ -1188,7 +1197,12 @@ public class CachedMusicService implements MusicService {
@Override
public Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
- return musicService.getBitmap(url, size, context, progressListener, task);
+ Bitmap bitmap = FileUtil.getMiscBitmap(context, url, size);
+ if(bitmap != null) {
+ return bitmap;
+ } else {
+ return musicService.getBitmap(url, size, context, progressListener, task);
+ }
}
@Override
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
index 2df290cf..f9e2bfb1 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
@@ -23,6 +23,7 @@ import android.util.Log;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.CastDevice;
+import com.google.android.gms.cast.CastStatusCodes;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
@@ -66,17 +67,12 @@ public class ChromeCastController extends RemoteController {
private boolean isStopping = false;
private Runnable afterUpdateComplete = null;
- private ServerProxy proxy;
- private String rootLocation;
private RemoteMediaPlayer mediaPlayer;
private double gain = 0.5;
public ChromeCastController(DownloadService downloadService, CastDevice castDevice) {
- this.downloadService = downloadService;
+ super(downloadService);
this.castDevice = castDevice;
-
- SharedPreferences prefs = Util.getPreferences(downloadService);
- rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
}
@Override
@@ -287,49 +283,7 @@ public class ChromeCastController extends RemoteController {
try {
MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url;
- // Offline, use file proxy
- if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
- if(proxy == null) {
- proxy = new FileProxy(downloadService);
- proxy.start();
- }
-
- // Offline song
- if(song.getId().indexOf(rootLocation) != -1) {
- url = proxy.getPublicAddress(song.getId());
- } else {
- // Playing online song in offline mode
- url = proxy.getPublicAddress(currentPlaying.getCompleteFile().getPath());
- }
- } else {
- // Check if we want a proxy going still
- if(Util.isCastProxy(downloadService)) {
- if(proxy instanceof FileProxy) {
- proxy.stop();
- proxy = null;
- }
-
- if(proxy == null) {
- proxy = createWebProxy();
- proxy.start();
- }
- } else if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
-
- if(song.isVideo()) {
- url = musicService.getHlsUrl(song.getId(), currentPlaying.getBitRate(), downloadService);
- } else {
- url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
- }
-
- // If proxy is going, it is a WebProxy
- if(proxy != null) {
- url = proxy.getPublicAddress(url);
- }
- }
+ String url = getStreamUrl(musicService, currentPlaying);
// Setup song/video information
MediaMetadata meta = new MediaMetadata(song.isVideo() ? MediaMetadata.MEDIA_TYPE_MOVIE : MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
@@ -390,6 +344,8 @@ public class ChromeCastController extends RemoteController {
public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
if (result.getStatus().isSuccess()) {
// Handled in other handler
+ } else if(result.getStatus().getStatusCode() == CastStatusCodes.REPLACED) {
+ Log.w(TAG, "Request was replaced: " + currentPlaying.toString());
} else {
Log.e(TAG, "Failed to load: " + result.getStatus().toString());
failedLoad();
@@ -506,7 +462,9 @@ public class ChromeCastController extends RemoteController {
break;
case MediaStatus.PLAYER_STATE_IDLE:
if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
- downloadService.onSongCompleted();
+ if(downloadService.getPlayerState() != PlayerState.PREPARING) {
+ downloadService.onSongCompleted();
+ }
} else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_INTERRUPTED) {
if (downloadService.getPlayerState() != PlayerState.PREPARING) {
downloadService.setPlayerState(PlayerState.PREPARING);
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java b/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
index 24890057..143be330 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
@@ -83,9 +83,6 @@ public class DLNAController extends RemoteController {
SubscriptionCallback callback;
boolean supportsSeek = false;
boolean supportsSetupNext = false;
-
- private ServerProxy proxy;
- String rootLocation = "";
boolean error = false;
final AtomicLong lastUpdate = new AtomicLong();
@@ -108,12 +105,9 @@ public class DLNAController extends RemoteController {
};
public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) {
- this.downloadService = downloadService;
+ super(downloadService);
this.controlPoint = controlPoint;
this.device = device;
-
- SharedPreferences prefs = Util.getPreferences(downloadService);
- rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
nextSupported = true;
}
@@ -473,49 +467,7 @@ public class DLNAController extends RemoteController {
// Get url for entry
MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url;
- // In offline mode or playing offline song
- if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
- if(proxy == null) {
- proxy = new FileProxy(downloadService);
- proxy.start();
- }
-
- // Offline song
- if(song.getId().indexOf(rootLocation) != -1) {
- url = proxy.getPublicAddress(song.getId());
- } else {
- // Playing online song in offline mode
- url = proxy.getPublicAddress(downloadFile.getCompleteFile().getPath());
- }
- } else {
- // Check if we want a proxy going still
- if(Util.isCastProxy(downloadService)) {
- if(proxy instanceof FileProxy) {
- proxy.stop();
- proxy = null;
- }
-
- if(proxy == null) {
- proxy = createWebProxy();
- proxy.start();
- }
- } else if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
-
- if(song.isVideo()) {
- url = musicService.getHlsUrl(song.getId(), downloadFile.getBitRate(), downloadService);
- } else {
- url = musicService.getMusicUrl(downloadService, song, downloadFile.getBitRate());
- }
-
- // If proxy is going, it is a WebProxy
- if(proxy != null) {
- url = proxy.getPublicAddress(url);
- }
- }
+ String url = getStreamUrl(musicService, downloadFile);
// Create metadata for entry
Item track;
@@ -585,7 +537,7 @@ public class DLNAController extends RemoteController {
Log.w(TAG, "Metadata generation failed", e);
}
- return new Pair<String, String>(url, metadata);
+ return new Pair<>(url, metadata);
}
private void failedLoad() {
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java
index d1c594ce..30e06982 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadFile.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.HttpURLConnection;
import android.content.Context;
import android.net.wifi.WifiManager;
@@ -39,15 +40,6 @@ import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.util.CacheCleaner;
import github.daneren2005.serverproxy.BufferFile;
-import org.apache.http.Header;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
public class DownloadFile implements BufferFile {
private static final String TAG = DownloadFile.class.getSimpleName();
private static final int MAX_FAILURES = 5;
@@ -467,17 +459,15 @@ public class DownloadFile implements BufferFile {
}
if(compare) {
// Attempt partial HTTP GET, appending to the file if it exists.
- HttpResponse response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
- Header contentLengthHeader = response.getFirstHeader("Content-Length");
- if(contentLengthHeader != null) {
- String contentLengthString = contentLengthHeader.getValue();
- if(contentLengthString != null) {
- Log.i(TAG, "Content Length: " + contentLengthString);
- contentLength = Long.parseLong(contentLengthString);
- }
+ HttpURLConnection connection = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
+ long contentLength = connection.getContentLength();
+ if(contentLength > 0) {
+ Log.i(TAG, "Content Length: " + contentLength);
+ DownloadFile.this.contentLength = contentLength;
}
- in = response.getEntity().getContent();
- boolean partial = response.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT;
+
+ in = connection.getInputStream();
+ boolean partial = connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL;
if (partial) {
Log.i(TAG, "Executed partial HTTP GET, skipping " + partialFile.length() + " bytes");
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
index a6bbc327..1cf482ca 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
@@ -1469,7 +1469,7 @@ public class DownloadService extends Service {
Notifications.hidePlayingNotification(this, this, handler);
}
if(mRemoteControl != null) {
- mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState());
+ mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState(), getCurrentPlayingIndex(), size());
}
if (playerState == STARTED) {
@@ -2842,6 +2842,8 @@ public class DownloadService extends Service {
final Integer duration = getPlayerDuration();
final boolean isSeekable = isSeekable();
final int position = getPlayerPosition();
+ final int index = getCurrentPlayingIndex();
+ final int queueSize = size();
synchronized(onSongChangedListeners) {
for (final OnSongChangedListener listener : onSongChangedListeners) {
@@ -2861,7 +2863,7 @@ public class DownloadService extends Service {
@Override
public void run() {
if(mRemoteControl != null) {
- mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState());
+ mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState(), index, queueSize);
}
}
});
diff --git a/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java b/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
index 82ef45e1..b9f40f32 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
@@ -48,7 +48,7 @@ public class JukeboxController extends RemoteController {
private float gain = 0.5f;
public JukeboxController(DownloadService downloadService, Handler handler) {
- this.downloadService = downloadService;
+ super(downloadService);
this.handler = handler;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
index 1e275108..6a58e340 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
@@ -18,15 +18,14 @@
*/
package github.daneren2005.dsub.service;
+import java.net.HttpURLConnection;
import java.util.List;
-import org.apache.http.HttpResponse;
import android.content.Context;
import android.graphics.Bitmap;
import github.daneren2005.dsub.domain.ArtistInfo;
-import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.ChatMessage;
import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.Indexes;
@@ -104,7 +103,7 @@ public interface MusicService {
Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception;
- HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception;
+ HttpURLConnection getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception;
String getMusicUrl(Context context, MusicDirectory.Entry song, int maxBitrate) throws Exception;
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 2c439ec4..da6c37f1 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -21,6 +21,7 @@ package github.daneren2005.dsub.service;
import java.io.File;
import java.io.Reader;
import java.io.FileReader;
+import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -34,8 +35,6 @@ import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.util.Log;
-import org.apache.http.HttpResponse;
-
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.ChatMessage;
@@ -227,7 +226,7 @@ public class OfflineMusicService implements MusicService {
}
@Override
- public HttpResponse getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
+ public HttpURLConnection getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
throw new OfflineException(ERRORMSG);
}
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 6ccf562c..913e30bf 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -24,52 +24,25 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.params.ConnManagerParams;
-import org.apache.http.conn.params.ConnPerRouteBean;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.scheme.SocketFactory;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.ExecutionContext;
-import org.apache.http.protocol.HttpContext;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.os.Looper;
import android.util.Log;
+import com.google.android.gms.security.ProviderInstaller;
+
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.*;
import github.daneren2005.dsub.fragments.MainFragment;
@@ -100,9 +73,6 @@ import github.daneren2005.dsub.service.parser.StarredListParser;
import github.daneren2005.dsub.service.parser.TopSongsParser;
import github.daneren2005.dsub.service.parser.UserParser;
import github.daneren2005.dsub.service.parser.VideosParser;
-import github.daneren2005.dsub.service.ssl.SSLSocketFactory;
-import github.daneren2005.dsub.service.ssl.TrustSelfSignedStrategy;
-import github.daneren2005.dsub.util.BackgroundTask;
import github.daneren2005.dsub.util.Pair;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Constants;
@@ -111,19 +81,23 @@ import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SongDBHandler;
import github.daneren2005.dsub.util.Util;
import java.io.*;
+import java.util.Map;
import java.util.zip.GZIPInputStream;
-/**
- * @author Sindre Mehus
- */
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
public class RESTMusicService implements MusicService {
private static final String TAG = RESTMusicService.class.getSimpleName();
- private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000;
private static final int SOCKET_READ_TIMEOUT_DEFAULT = 10 * 1000;
private static final int SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000;
- private static final int SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS = 60 * 1000;
private static final int SOCKET_READ_TIMEOUT_GET_PLAYLIST = 60 * 1000;
// Allow 20 seconds extra timeout per MB offset.
@@ -132,51 +106,46 @@ public class RESTMusicService implements MusicService {
private static final int HTTP_REQUEST_MAX_ATTEMPTS = 5;
private static final long REDIRECTION_CHECK_INTERVAL_MILLIS = 60L * 60L * 1000L;
- private final DefaultHttpClient httpClient;
+ private SSLSocketFactory sslSocketFactory;
+ private HostnameVerifier selfSignedHostnameVerifier;
private long redirectionLastChecked;
private int redirectionNetworkType = -1;
private String redirectFrom;
private String redirectTo;
- private final ThreadSafeClientConnManager connManager;
private Integer instance;
+ private boolean hasInstalledGoogleSSL = false;
public RESTMusicService() {
+ TrustManager[] trustAllCerts = new TrustManager[]{
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ }
+ };
+ try {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+ sslSocketFactory = sslContext.getSocketFactory();
+ } catch (Exception e) {
+ }
- // Create and initialize default HTTP parameters
- HttpParams params = new BasicHttpParams();
- ConnManagerParams.setMaxTotalConnections(params, 20);
- ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(20));
- HttpConnectionParams.setConnectionTimeout(params, SOCKET_CONNECT_TIMEOUT);
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_DEFAULT);
-
- // Turn off stale checking. Our connections break all the time anyway,
- // and it's not worth it to pay the penalty of checking every time.
- HttpConnectionParams.setStaleCheckingEnabled(params, false);
-
- // Create and initialize scheme registry
- SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schemeRegistry.register(new Scheme("https", createSSLSocketFactory(), 443));
-
- // Create an HttpClient with the ThreadSafeClientConnManager.
- // This connection manager must be used if more than one thread will
- // be using the HttpClient.
- connManager = new ThreadSafeClientConnManager(params, schemeRegistry);
- httpClient = new DefaultHttpClient(connManager, params);
- }
-
- private SocketFactory createSSLSocketFactory() {
- try {
- return new SSLSocketFactory(new TrustSelfSignedStrategy(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- } catch (Throwable x) {
- Log.e(TAG, "Failed to create custom SSL socket factory, using default.", x);
- return org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
- }
+ selfSignedHostnameVerifier = new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
}
@Override
public void ping(Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "ping", null);
+ Reader reader = getReader(context, progressListener, "ping");
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -187,7 +156,7 @@ public class RESTMusicService implements MusicService {
@Override
public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getLicense", null);
+ Reader reader = getReader(context, progressListener, "getLicense");
try {
ServerInfo serverInfo = new LicenseParser(context, getInstance(context)).parse(reader);
return serverInfo.isLicenseValid();
@@ -197,7 +166,7 @@ public class RESTMusicService implements MusicService {
}
public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getMusicFolders", null);
+ Reader reader = getReader(context, progressListener, "getMusicFolders");
try {
return new MusicFoldersParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -207,7 +176,7 @@ public class RESTMusicService implements MusicService {
@Override
public void startRescan(Context context, ProgressListener listener) throws Exception {
- Reader reader = getReader(context, listener, "startRescan", null);
+ Reader reader = getReader(context, listener, "startRescan");
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -217,7 +186,7 @@ public class RESTMusicService implements MusicService {
// Now check if still running
boolean done = false;
while(!done) {
- reader = getReader(context, null, "scanstatus", null);
+ reader = getReader(context, null, "scanstatus");
try {
boolean running = new ScanStatusParser(context, getInstance(context)).parse(reader, listener);
if(running) {
@@ -244,7 +213,7 @@ public class RESTMusicService implements MusicService {
parameterValues.add(musicFolderId);
}
- Reader reader = getReader(context, progressListener, Util.isTagBrowsing(context, getInstance(context)) ? "getArtists" : "getIndexes", null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, Util.isTagBrowsing(context, getInstance(context)) ? "getArtists" : "getIndexes", parameterNames, parameterValues);
try {
return new IndexesParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -290,7 +259,7 @@ public class RESTMusicService implements MusicService {
}
private MusicDirectory getMusicDirectoryImpl(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getMusicDirectory", null, "id", id);
+ Reader reader = getReader(context, progressListener, "getMusicDirectory", "id", id);
try {
return new MusicDirectoryParser(context, getInstance(context)).parse(name, reader, progressListener);
} finally {
@@ -300,7 +269,7 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getArtist", null, "id", id);
+ Reader reader = getReader(context, progressListener, "getArtist", "id", id);
try {
return new MusicDirectoryParser(context, getInstance(context)).parse(name, reader, progressListener);
} finally {
@@ -310,7 +279,7 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getAlbum", null, "id", id);
+ Reader reader = getReader(context, progressListener, "getAlbum", "id", id);
try {
return new MusicDirectoryParser(context, getInstance(context)).parse(name, reader, progressListener);
} finally {
@@ -334,7 +303,7 @@ public class RESTMusicService implements MusicService {
private SearchResult searchOld(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception {
List<String> parameterNames = Arrays.asList("any", "songCount");
List<Object> parameterValues = Arrays.<Object>asList(critera.getQuery(), critera.getSongCount());
- Reader reader = getReader(context, progressListener, "search", null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "search", parameterNames, parameterValues);
try {
return new SearchResultParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -366,7 +335,7 @@ public class RESTMusicService implements MusicService {
method = "search2";
}
}
- Reader reader = getReader(context, progressListener, method, null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, method, parameterNames, parameterValues);
try {
return new SearchResult2Parser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -376,10 +345,7 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getPlaylist(boolean refresh, String id, String name, Context context, ProgressListener progressListener) throws Exception {
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_PLAYLIST);
-
- Reader reader = getReader(context, progressListener, "getPlaylist", params, "id", id);
+ Reader reader = getReader(context, progressListener, "getPlaylist", "id", id, SOCKET_READ_TIMEOUT_GET_PLAYLIST);
try {
return new PlaylistParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -389,7 +355,7 @@ public class RESTMusicService implements MusicService {
@Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getPlaylists", null);
+ Reader reader = getReader(context, progressListener, "getPlaylists");
try {
return new PlaylistsParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -415,7 +381,7 @@ public class RESTMusicService implements MusicService {
parameterValues.add(getOfflineSongId(entry.getId(), context, progressListener));
}
- Reader reader = getReader(context, progressListener, "createPlaylist", null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "createPlaylist", parameterNames, parameterValues);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -425,7 +391,7 @@ public class RESTMusicService implements MusicService {
@Override
public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "deletePlaylist", null, "id", id);
+ Reader reader = getReader(context, progressListener, "deletePlaylist", "id", id);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -444,7 +410,7 @@ public class RESTMusicService implements MusicService {
names.add("songIdToAdd");
values.add(getOfflineSongId(song.getId(), context, progressListener));
}
- Reader reader = getReader(context, progressListener, "updatePlaylist", null, names, values);
+ Reader reader = getReader(context, progressListener, "updatePlaylist", names, values);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -463,7 +429,7 @@ public class RESTMusicService implements MusicService {
names.add("songIndexToRemove");
values.add(song);
}
- Reader reader = getReader(context, progressListener, "updatePlaylist", null, names, values);
+ Reader reader = getReader(context, progressListener, "updatePlaylist", names, values);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -488,7 +454,7 @@ public class RESTMusicService implements MusicService {
names.add("songIndexToRemove");
values.add(i);
}
- Reader reader = getReader(context, progressListener, "updatePlaylist", null, names, values);
+ Reader reader = getReader(context, progressListener, "updatePlaylist", names, values);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -499,7 +465,7 @@ public class RESTMusicService implements MusicService {
@Override
public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.8", "Updating playlists is not supported.");
- Reader reader = getReader(context, progressListener, "updatePlaylist", null, Arrays.asList("playlistId", "name", "comment", "public"), Arrays.<Object>asList(id, name, comment, pub));
+ Reader reader = getReader(context, progressListener, "updatePlaylist", Arrays.asList("playlistId", "name", "comment", "public"), Arrays.<Object>asList(id, name, comment, pub));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -509,7 +475,7 @@ public class RESTMusicService implements MusicService {
@Override
public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getLyrics", null, Arrays.asList("artist", "title"), Arrays.<Object>asList(artist, title));
+ Reader reader = getReader(context, progressListener, "getLyrics", Arrays.asList("artist", "title"), Arrays.<Object>asList(artist, title));
try {
return new LyricsParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -528,10 +494,10 @@ public class RESTMusicService implements MusicService {
Reader reader;
if(time > 0){
checkServerVersion(context, "1.8", "Scrobbling with a time not supported.");
- reader = getReader(context, progressListener, "scrobble", null, Arrays.asList("id", "submission", "time"), Arrays.<Object>asList(id, submission, time));
+ reader = getReader(context, progressListener, "scrobble", Arrays.asList("id", "submission", "time"), Arrays.<Object>asList(id, submission, time));
}
else
- reader = getReader(context, progressListener, "scrobble", null, Arrays.asList("id", "submission"), Arrays.<Object>asList(id, submission));
+ reader = getReader(context, progressListener, "scrobble", Arrays.asList("id", "submission"), Arrays.<Object>asList(id, submission));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -572,7 +538,7 @@ public class RESTMusicService implements MusicService {
method = "getAlbumList";
}
- Reader reader = getReader(context, progressListener, method, null, names, values, true);
+ Reader reader = getReader(context, progressListener, method, names, values, true);
try {
return new EntryListParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -638,7 +604,7 @@ public class RESTMusicService implements MusicService {
method = "getAlbumList";
}
- Reader reader = getReader(context, progressListener, method, null, names, values, true);
+ Reader reader = getReader(context, progressListener, method, names, values, true);
try {
return new EntryListParser(context, instance).parse(reader, progressListener);
} finally {
@@ -674,7 +640,7 @@ public class RESTMusicService implements MusicService {
method = "getNewaddedSongs";
}
- Reader reader = getReader(context, progressListener, method, null, names, values, true);
+ Reader reader = getReader(context, progressListener, method, names, values, true);
try {
return new EntryListParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -713,7 +679,7 @@ public class RESTMusicService implements MusicService {
}
}
- Reader reader = getReader(context, progressListener, method, null, names, values);
+ Reader reader = getReader(context, progressListener, method, names, values);
try {
return new RandomSongsParser(context, instance).parse(reader, progressListener);
} finally {
@@ -747,7 +713,7 @@ public class RESTMusicService implements MusicService {
method = "getStarred";
}
- Reader reader = getReader(context, progressListener, method, null, names, values, true);
+ Reader reader = getReader(context, progressListener, method, names, values, true);
try {
return new StarredListParser(context, instance).parse(reader, progressListener);
} finally {
@@ -757,10 +723,7 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getRandomSongs(int size, String musicFolderId, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
-
- List<String> names = new ArrayList<String>();
+ List<String> names = new ArrayList<String>();
List<Object> values = new ArrayList<Object>();
names.add("size");
@@ -799,7 +762,7 @@ public class RESTMusicService implements MusicService {
values.add(endYear);
}
- Reader reader = getReader(context, progressListener, "getRandomSongs", params, names, values);
+ Reader reader = getReader(context, progressListener, "getRandomSongs", names, values);
try {
return new RandomSongsParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -829,87 +792,24 @@ public class RESTMusicService implements MusicService {
@Override
public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
-
// Synchronize on the entry so that we don't download concurrently for the same song.
synchronized (entry) {
+ String url = getRestUrl(context, "getCoverArt");
+ List<String> parameterNames = Arrays.asList("id");
+ List<Object> parameterValues = Arrays.<Object>asList(entry.getCoverArt());
- // Use cached file, if existing.
- Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size);
- if (bitmap != null) {
- return bitmap;
- }
-
- String url = getRestUrl(context, "getCoverArt");
-
- InputStream in = null;
- try {
- List<String> parameterNames = Arrays.asList("id");
- List<Object> parameterValues = Arrays.<Object>asList(entry.getCoverArt());
- HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener, task);
-
- in = entity.getContent();
- Header contentEncoding = entity.getContentEncoding();
- if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
- in = new GZIPInputStream(in);
- }
-
- // If content type is XML, an error occured. Get it.
- String contentType = Util.getContentType(entity);
- if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) {
- new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8));
- return null; // Never reached.
- }
-
- byte[] bytes = Util.toByteArray(in);
-
- // Handle case where partial was downloaded before being cancelled
- if(task != null && task.isCancelled()) {
- return null;
- }
-
- OutputStream out = null;
- try {
- out = new FileOutputStream(FileUtil.getAlbumArtFile(context, entry));
- out.write(bytes);
- } finally {
- Util.close(out);
- }
-
- // Size == 0 -> only want to download
- if(size == 0) {
- return null;
- } else {
- return FileUtil.getSampledBitmap(bytes, size);
- }
- } finally {
- Util.close(in);
- }
+ return getBitmapFromUrl(context, url, parameterNames, parameterValues, size, FileUtil.getAlbumArtFile(context, entry), true, progressListener, task);
}
}
@Override
- public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
-
+ public HttpURLConnection getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
String url = getRestUrl(context, "stream");
-
- // Set socket read timeout. Note: The timeout increases as the offset gets larger. This is
- // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server.
- // In that case, the server uses a long time before sending any data, causing the client to time out.
- HttpParams params = new BasicHttpParams();
- int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE);
- HttpConnectionParams.setSoTimeout(params, timeout);
-
- // Add "Range" header if offset is given.
- List<Header> headers = new ArrayList<Header>();
- if (offset > 0) {
- headers.add(new BasicHeader("Range", "bytes=" + offset + "-"));
- }
-
List<String> parameterNames = new ArrayList<String>();
parameterNames.add("id");
parameterNames.add("maxBitRate");
- List<Object> parameterValues = new ArrayList<Object>();
+ List<Object> parameterValues = new ArrayList<>();
parameterValues.add(song.getId());
parameterValues.add(maxBitrate);
@@ -929,24 +829,32 @@ public class RESTMusicService implements MusicService {
parameterValues.add("raw");
}
}
- HttpResponse response = getResponseForURL(context, url, params, parameterNames, parameterValues, headers, null, task, false);
- // If content type is XML, an error occurred. Get it.
- String contentType = Util.getContentType(response.getEntity());
- if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) {
- InputStream in = response.getEntity().getContent();
- Header contentEncoding = response.getEntity().getContentEncoding();
- if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
- in = new GZIPInputStream(in);
+ // Add "Range" header if offset is given
+ Map<String, String> headers = new HashMap<>();
+ if (offset > 0) {
+ headers.put("Range", "bytes=" + offset + "-");
+ }
+
+ // Set socket read timeout. Note: The timeout increases as the offset gets larger. This is
+ // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server.
+ // In that case, the server uses a long time before sending any data, causing the client to time out.
+ int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE);
+ HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, headers, timeout);
+
+ // If content type is XML, an error occurred. Get it.
+ String contentType = connection.getContentType();
+ if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) {
+ InputStream in = getInputStreamFromConnection(connection);
+
+ try {
+ new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8));
+ } finally {
+ Util.close(in);
}
- try {
- new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8));
- } finally {
- Util.close(in);
- }
- }
+ }
- return response;
+ return connection;
}
@Override
@@ -1057,7 +965,7 @@ public class RESTMusicService implements MusicService {
private RemoteStatus executeJukeboxCommand(Context context, ProgressListener progressListener, List<String> parameterNames, List<Object> parameterValues) throws Exception {
checkServerVersion(context, "1.7", "Jukebox not supported.");
- Reader reader = getReader(context, progressListener, "jukeboxControl", null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "jukeboxControl", parameterNames, parameterValues);
try {
return new JukeboxStatusParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1096,7 +1004,7 @@ public class RESTMusicService implements MusicService {
}
}
- Reader reader = getReader(context, progressListener, starred ? "star" : "unstar", null, names, values);
+ Reader reader = getReader(context, progressListener, starred ? "star" : "unstar", names, values);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1108,7 +1016,7 @@ public class RESTMusicService implements MusicService {
public List<Share> getShares(Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.6", "Shares not supported.");
- Reader reader = getReader(context, progressListener, "getShares", null);
+ Reader reader = getReader(context, progressListener, "getShares");
try {
return new ShareParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1136,7 +1044,7 @@ public class RESTMusicService implements MusicService {
parameterValues.add(expires);
}
- Reader reader = getReader(context, progressListener, "createShare", null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "createShare", parameterNames, parameterValues);
try {
return new ShareParser(context, getInstance(context)).parse(reader, progressListener);
}
@@ -1149,16 +1057,13 @@ public class RESTMusicService implements MusicService {
public void deleteShare(String id, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.6", "Shares not supported.");
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
-
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
parameterNames.add("id");
parameterValues.add(id);
- Reader reader = getReader(context, progressListener, "deleteShare", params, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "deleteShare", parameterNames, parameterValues);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
@@ -1172,9 +1077,6 @@ public class RESTMusicService implements MusicService {
public void updateShare(String id, String description, Long expires, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.6", "Updating share not supported.");
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
-
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
@@ -1189,7 +1091,7 @@ public class RESTMusicService implements MusicService {
parameterNames.add("expires");
parameterValues.add(expires);
- Reader reader = getReader(context, progressListener, "updateShare", params, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "updateShare", parameterNames, parameterValues);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
}
@@ -1202,16 +1104,13 @@ public class RESTMusicService implements MusicService {
public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.2", "Chat not supported.");
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
-
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
parameterNames.add("since");
parameterValues.add(since);
- Reader reader = getReader(context, progressListener, "getChatMessages", params, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "getChatMessages", parameterNames, parameterValues);
try {
return new ChatMessageParser(context, getInstance(context)).parse(reader, progressListener);
@@ -1224,16 +1123,13 @@ public class RESTMusicService implements MusicService {
public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.2", "Chat not supported.");
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
-
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
parameterNames.add("message");
parameterValues.add(message);
- Reader reader = getReader(context, progressListener, "addChatMessage", params, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "addChatMessage", parameterNames, parameterValues);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
@@ -1246,7 +1142,7 @@ public class RESTMusicService implements MusicService {
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Genres not supported.");
- Reader reader = getReader(context, progressListener, "getGenres", null);
+ Reader reader = getReader(context, progressListener, "getGenres");
try {
return new GenreParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1258,9 +1154,6 @@ public class RESTMusicService implements MusicService {
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Genres not supported.");
- HttpParams params = new BasicHttpParams();
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
-
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
@@ -1281,7 +1174,7 @@ public class RESTMusicService implements MusicService {
}
}
- Reader reader = getReader(context, progressListener, "getSongsByGenre", params, parameterNames, parameterValues, true);
+ Reader reader = getReader(context, progressListener, "getSongsByGenre", parameterNames, parameterValues, true);
try {
return new RandomSongsParser(context, instance).parse(reader, progressListener);
} finally {
@@ -1300,7 +1193,7 @@ public class RESTMusicService implements MusicService {
parameterValues.add(size);
String method = ServerInfo.isMadsonic(context, getInstance(context)) ? "getTopTrackSongs" : "getTopSongs";
- Reader reader = getReader(context, progressListener, method, null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, method, parameterNames, parameterValues);
try {
return new TopSongsParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1312,7 +1205,7 @@ public class RESTMusicService implements MusicService {
public List<PodcastChannel> getPodcastChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.6", "Podcasts not supported.");
- Reader reader = getReader(context, progressListener, "getPodcasts", null, Arrays.asList("includeEpisodes"), Arrays.<Object>asList("false"));
+ Reader reader = getReader(context, progressListener, "getPodcasts", Arrays.asList("includeEpisodes"), Arrays.<Object>asList("false"));
try {
List<PodcastChannel> channels = new PodcastChannelParser(context, getInstance(context)).parse(reader, progressListener);
@@ -1334,7 +1227,7 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getPodcastEpisodes(boolean refresh, String id, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getPodcasts", null, Arrays.asList("id"), Arrays.<Object>asList(id));
+ Reader reader = getReader(context, progressListener, "getPodcasts", Arrays.asList("id"), Arrays.<Object>asList(id));
try {
return new PodcastEntryParser(context, getInstance(context)).parse(id, reader, progressListener);
} finally {
@@ -1344,7 +1237,7 @@ public class RESTMusicService implements MusicService {
@Override
public MusicDirectory getNewestPodcastEpisodes(boolean refresh, Context context, ProgressListener progressListener, int count) throws Exception {
- Reader reader = getReader(context, progressListener, "getNewestPodcasts", null, Arrays.asList("count"), Arrays.<Object>asList(count), true);
+ Reader reader = getReader(context, progressListener, "getNewestPodcasts", Arrays.asList("count"), Arrays.<Object>asList(count), true);
try {
return new PodcastEntryParser(context, getInstance(context)).parse(null, reader, progressListener);
@@ -1357,7 +1250,7 @@ public class RESTMusicService implements MusicService {
public void refreshPodcasts(Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Refresh podcasts not supported.");
- Reader reader = getReader(context, progressListener, "refreshPodcasts", null);
+ Reader reader = getReader(context, progressListener, "refreshPodcasts");
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1369,7 +1262,7 @@ public class RESTMusicService implements MusicService {
public void createPodcastChannel(String url, Context context, ProgressListener progressListener) throws Exception{
checkServerVersion(context, "1.9", "Creating podcasts not supported.");
- Reader reader = getReader(context, progressListener, "createPodcastChannel", null, "url", url);
+ Reader reader = getReader(context, progressListener, "createPodcastChannel", "url", url);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1381,7 +1274,7 @@ public class RESTMusicService implements MusicService {
public void deletePodcastChannel(String id, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Deleting podcasts not supported.");
- Reader reader = getReader(context, progressListener, "deletePodcastChannel", null, "id", id);
+ Reader reader = getReader(context, progressListener, "deletePodcastChannel", "id", id);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1393,7 +1286,7 @@ public class RESTMusicService implements MusicService {
public void downloadPodcastEpisode(String id, Context context, ProgressListener progressListener) throws Exception{
checkServerVersion(context, "1.9", "Downloading podcasts not supported.");
- Reader reader = getReader(context, progressListener, "downloadPodcastEpisode", null, "id", id);
+ Reader reader = getReader(context, progressListener, "downloadPodcastEpisode", "id", id);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1405,7 +1298,7 @@ public class RESTMusicService implements MusicService {
public void deletePodcastEpisode(String id, String parent, ProgressListener progressListener, Context context) throws Exception{
checkServerVersion(context, "1.9", "Deleting podcasts not supported.");
- Reader reader = getReader(context, progressListener, "deletePodcastEpisode", null, "id", id);
+ Reader reader = getReader(context, progressListener, "deletePodcastEpisode", "id", id);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1417,7 +1310,7 @@ public class RESTMusicService implements MusicService {
public void setRating(MusicDirectory.Entry entry, int rating, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.6", "Setting ratings not supported.");
- Reader reader = getReader(context, progressListener, "setRating", null, Arrays.asList("id", "rating"), Arrays.<Object>asList(entry.getId(), rating));
+ Reader reader = getReader(context, progressListener, "setRating", Arrays.asList("id", "rating"), Arrays.<Object>asList(entry.getId(), rating));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1429,7 +1322,7 @@ public class RESTMusicService implements MusicService {
public MusicDirectory getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Bookmarks not supported.");
- Reader reader = getReader(context, progressListener, "getBookmarks", null);
+ Reader reader = getReader(context, progressListener, "getBookmarks");
try {
return new BookmarkParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1441,7 +1334,7 @@ public class RESTMusicService implements MusicService {
public void createBookmark(MusicDirectory.Entry entry, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Creating bookmarks not supported.");
- Reader reader = getReader(context, progressListener, "createBookmark", null, Arrays.asList("id", "position", "comment"), Arrays.<Object>asList(entry.getId(), position, comment));
+ Reader reader = getReader(context, progressListener, "createBookmark", Arrays.asList("id", "position", "comment"), Arrays.<Object>asList(entry.getId(), position, comment));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1453,7 +1346,7 @@ public class RESTMusicService implements MusicService {
public void deleteBookmark(MusicDirectory.Entry entry, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Deleting bookmarks not supported.");
- Reader reader = getReader(context, progressListener, "deleteBookmark", null, Arrays.asList("id"), Arrays.<Object>asList(entry.getId()));
+ Reader reader = getReader(context, progressListener, "deleteBookmark", Arrays.asList("id"), Arrays.<Object>asList(entry.getId()));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1463,7 +1356,7 @@ public class RESTMusicService implements MusicService {
@Override
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getUser", null, Arrays.asList("username"), Arrays.<Object>asList(username));
+ Reader reader = getReader(context, progressListener, "getUser", Arrays.asList("username"), Arrays.<Object>asList(username));
try {
List<User> users = new UserParser(context, getInstance(context)).parse(reader, progressListener);
if(users.size() > 0) {
@@ -1481,7 +1374,7 @@ public class RESTMusicService implements MusicService {
public List<User> getUsers(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.8", "Getting user list is not supported");
- Reader reader = getReader(context, progressListener, "getUsers", null);
+ Reader reader = getReader(context, progressListener, "getUsers");
try {
return new UserParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1515,7 +1408,7 @@ public class RESTMusicService implements MusicService {
}
}
- Reader reader = getReader(context, progressListener, "createUser", null, names, values);
+ Reader reader = getReader(context, progressListener, "createUser", names, values);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1549,7 +1442,7 @@ public class RESTMusicService implements MusicService {
}
}
- Reader reader = getReader(context, progressListener, "updateUser", null, names, values);
+ Reader reader = getReader(context, progressListener, "updateUser", names, values);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1559,7 +1452,7 @@ public class RESTMusicService implements MusicService {
@Override
public void deleteUser(String username, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "deleteUser", null, Arrays.asList("username"), Arrays.<Object>asList(username));
+ Reader reader = getReader(context, progressListener, "deleteUser", Arrays.asList("username"), Arrays.<Object>asList(username));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1569,7 +1462,7 @@ public class RESTMusicService implements MusicService {
@Override
public void changeEmail(String username, String email, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "updateUser", null, Arrays.asList("username", "email"), Arrays.<Object>asList(username, email));
+ Reader reader = getReader(context, progressListener, "updateUser", Arrays.asList("username", "email"), Arrays.<Object>asList(username, email));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1579,7 +1472,7 @@ public class RESTMusicService implements MusicService {
@Override
public void changePassword(String username, String password, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "changePassword", null, Arrays.asList("username", "password"), Arrays.<Object>asList(username, password));
+ Reader reader = getReader(context, progressListener, "changePassword", Arrays.asList("username", "password"), Arrays.<Object>asList(username, password));
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1597,55 +1490,11 @@ public class RESTMusicService implements MusicService {
// Synchronize on the username so that we don't download concurrently for
// the same user.
synchronized (username) {
- // Use cached file, if existing.
- Bitmap bitmap = FileUtil.getAvatarBitmap(context, username, size);
- if(bitmap != null) {
- return bitmap;
- }
-
String url = Util.getRestUrl(context, "getAvatar");
- InputStream in = null;
- try
- {
- List<String> parameterNames;
- List<Object> parameterValues;
-
- parameterNames = Collections.singletonList("username");
- parameterValues = Arrays.<Object>asList(username);
-
- HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener, task);
- in = entity.getContent();
- Header contentEncoding = entity.getContentEncoding();
- if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
- in = new GZIPInputStream(in);
- }
-
- // If content type is XML, an error occurred. Get it.
- String contentType = Util.getContentType(entity);
- if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) {
- new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8));
- return null; // Never reached.
- }
-
- byte[] bytes = Util.toByteArray(in);
- if(task != null && task.isCancelled()) {
- // Handle case where partial is downloaded and cancelled
- return null;
- }
+ List<String> parameterNames = Collections.singletonList("username");
+ List<Object> parameterValues = Arrays.<Object>asList(username);
- OutputStream out = null;
- try {
- out = new FileOutputStream(FileUtil.getAvatarFile(context, username));
- out.write(bytes);
- } finally {
- Util.close(out);
- }
-
- return FileUtil.getSampledBitmap(bytes, size, false);
- }
- finally {
- Util.close(in);
- }
+ return getBitmapFromUrl(context, url, parameterNames, parameterValues, size, FileUtil.getAvatarFile(context, username), false, progressListener, task);
}
}
@@ -1665,7 +1514,7 @@ public class RESTMusicService implements MusicService {
method = "getArtistInfo";
}
- Reader reader = getReader(context, progressListener, method, null, Arrays.asList("id", "includeNotPresent"), Arrays.<Object>asList(id, "true"));
+ Reader reader = getReader(context, progressListener, method, Arrays.asList("id", "includeNotPresent"), Arrays.<Object>asList(id, "true"));
try {
return new ArtistInfoParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1677,53 +1526,13 @@ public class RESTMusicService implements MusicService {
public Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
// Synchronize on the url so that we don't download concurrently
synchronized (url) {
- // Use cached file, if existing.
- Bitmap bitmap = FileUtil.getMiscBitmap(context, url, size);
- if(bitmap != null) {
- return bitmap;
- }
-
- InputStream in = null;
- try {
- HttpEntity entity = getEntityForURL(context, url, null, null, null, progressListener, task);
- in = entity.getContent();
- Header contentEncoding = entity.getContentEncoding();
- if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
- in = new GZIPInputStream(in);
- }
-
- // If content type is XML, an error occurred. Get it.
- String contentType = Util.getContentType(entity);
- if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) {
- new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8));
- return null; // Never reached.
- }
-
- byte[] bytes = Util.toByteArray(in);
- if(task != null && task.isCancelled()) {
- // Handle case where partial is downloaded and cancelled
- return null;
- }
-
- OutputStream out = null;
- try {
- out = new FileOutputStream(FileUtil.getMiscFile(context, url));
- out.write(bytes);
- } finally {
- Util.close(out);
- }
-
- return FileUtil.getSampledBitmap(bytes, size, false);
- }
- finally {
- Util.close(in);
- }
+ return getBitmapFromUrl(context, url, null, null, size, FileUtil.getMiscFile(context, url), false, progressListener, task);
}
}
@Override
public MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getVideos", null, true);
+ Reader reader = getReader(context, progressListener, "getVideos");
try {
return new VideosParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1747,7 +1556,7 @@ public class RESTMusicService implements MusicService {
parameterNames.add("position");
parameterValues.add(position);
- Reader reader = getReader(context, progressListener, "savePlayQueue", null, parameterNames, parameterValues);
+ Reader reader = getReader(context, progressListener, "savePlayQueue", parameterNames, parameterValues);
try {
new ErrorParser(context, getInstance(context)).parse(reader);
} finally {
@@ -1757,7 +1566,7 @@ public class RESTMusicService implements MusicService {
@Override
public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception {
- Reader reader = getReader(context, progressListener, "getPlayQueue", null);
+ Reader reader = getReader(context, progressListener, "getPlayQueue");
try {
return new PlayQueueParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1769,7 +1578,7 @@ public class RESTMusicService implements MusicService {
public List<InternetRadioStation> getInternetRadioStations(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", null);
- Reader reader = getReader(context, progressListener, "getInternetRadioStations", null);
+ Reader reader = getReader(context, progressListener, "getInternetRadioStations");
try {
return new InternetRadioStationParser(context, getInstance(context)).parse(reader, progressListener);
} finally {
@@ -1889,210 +1698,237 @@ public class RESTMusicService implements MusicService {
this.instance = instance;
}
- private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception {
- return getReader(context, progressListener, method, requestParams, false);
+ protected Bitmap getBitmapFromUrl(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int size, File saveToFile, boolean allowUnscaled, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
+ InputStream in = null;
+ try {
+ HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, progressListener, true);
+ in = getInputStreamFromConnection(connection);
+
+ String contentType = connection.getContentType();
+ if (contentType != null && (contentType.startsWith("text/xml") || contentType.startsWith("text/html"))) {
+ new ErrorParser(context, getInstance(context)).parse(new InputStreamReader(in, Constants.UTF_8));
+ }
+
+ byte[] bytes = Util.toByteArray(in);
+
+ // Handle case where partial was downloaded before being cancelled
+ if(task != null && task.isCancelled()) {
+ return null;
+ }
+
+ OutputStream out = null;
+ try {
+ out = new FileOutputStream(saveToFile);
+ out.write(bytes);
+ } finally {
+ Util.close(out);
+ }
+
+ // Size == 0 -> only want to download
+ if(size == 0) {
+ return null;
+ } else {
+ return FileUtil.getSampledBitmap(bytes, size, allowUnscaled);
+ }
+ } finally {
+ Util.close(in);
+ }
}
- private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams, boolean throwsError) throws Exception {
- return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList(), throwsError);
- }
- private Reader getReader(Context context, ProgressListener progressListener, String method,
- HttpParams requestParams, String parameterName, Object parameterValue) throws Exception {
- return getReader(context, progressListener, method, requestParams, Arrays.asList(parameterName), Arrays.<Object>asList(parameterValue));
- }
+ // Helper classes to get a reader for the request
+ private Reader getReader(Context context, ProgressListener progressListener, String method) throws Exception {
+ return getReader(context, progressListener, method, (List<String>)null, null);
+ }
- private Reader getReader(Context context, ProgressListener progressListener, String method,
- HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues) throws Exception {
- return getReader(context, progressListener, method, requestParams, parameterNames, parameterValues, false);
+ private Reader getReader(Context context, ProgressListener progressListener, String method, String parameterName, Object parameterValue) throws Exception {
+ return getReader(context, progressListener, method, parameterName, parameterValue, 0);
}
- private Reader getReader(Context context, ProgressListener progressListener, String method,
- HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues, boolean throwErrors) throws Exception {
+ private Reader getReader(Context context, ProgressListener progressListener, String method, String parameterName, Object parameterValue, int minNetworkTimeout) throws Exception {
+ return getReader(context, progressListener, method, Arrays.asList(parameterName), Arrays.asList(parameterValue), minNetworkTimeout);
+ }
+ private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues) throws Exception {
+ return getReader(context, progressListener, method, parameterNames, parameterValues, 0);
+ }
+ private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout) throws Exception {
+ return getReader(context, progressListener, method, parameterNames, parameterValues, minNetworkTimeout, false);
+ }
+ private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues, boolean throwErrors) throws Exception {
+ return getReader(context, progressListener, method, parameterNames, parameterValues, 0, throwErrors);
+ }
+ private Reader getReader(Context context, ProgressListener progressListener, String method, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, boolean throwErrors) throws Exception {
if (progressListener != null) {
progressListener.updateProgress(R.string.service_connecting);
}
String url = getRestUrl(context, method);
- return getReaderForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, throwErrors);
+ return getReaderForURL(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, throwErrors);
}
- private Reader getReaderForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,
- List<Object> parameterValues, ProgressListener progressListener) throws Exception {
- return getReaderForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, true);
+ private Reader getReaderForURL(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener) throws Exception {
+ return getReaderForURL(context, url, parameterNames, parameterValues, progressListener, true);
}
- private Reader getReaderForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,
- List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception {
- HttpEntity entity = getEntityForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, throwErrors);
- if (entity == null) {
- throw new RuntimeException("No entity received for URL " + url);
- }
+ private Reader getReaderForURL(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ return getReaderForURL(context, url, parameterNames, parameterValues, 0, progressListener, throwErrors);
+ }
+ private Reader getReaderForURL(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ InputStream in = getInputStream(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, throwErrors);
+ return new InputStreamReader(in, Constants.UTF_8);
+ }
- InputStream in = entity.getContent();
- Header contentEncoding = entity.getContentEncoding();
- if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
+ // Helper classes to open a connection to a server
+ private InputStream getInputStream(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, boolean throwsErrors) throws Exception {
+ return getInputStream(context, url, parameterNames, parameterValues, 0, progressListener, throwsErrors);
+ }
+ private InputStream getInputStream(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwsErrors) throws Exception {
+ HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, throwsErrors);
+ return getInputStreamFromConnection(connection);
+ }
+ private InputStream getInputStreamFromConnection(HttpURLConnection connection) throws Exception {
+ InputStream in = connection.getInputStream();
+ if("gzip".equals(connection.getContentEncoding())) {
in = new GZIPInputStream(in);
}
- return new InputStreamReader(in, Constants.UTF_8);
- }
- private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,
- List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ return in;
+ }
- return getEntityForURL(context, url, requestParams, parameterNames, parameterValues, progressListener, null, throwErrors);
+ private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, Map<String, String> headers, int minNetworkTimeout) throws Exception {
+ return getConnection(context, url, parameterNames, parameterValues, headers, minNetworkTimeout, null, true);
}
+ private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ return getConnection(context, url, parameterNames, parameterValues, 0, progressListener, throwErrors);
+ }
+ private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ return getConnection(context, url, parameterNames, parameterValues, null, minNetworkTimeout, progressListener, throwErrors);
+ }
+ private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, Map<String, String> headers, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ if(throwErrors) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, SOCKET_READ_TIMEOUT_DEFAULT + ""));
+ return getConnectionDirect(context, url, parameterNames, parameterValues, headers, Math.max(minNetworkTimeout, networkTimeout));
+ } else {
+ return getConnection(context, url, parameterNames, parameterValues, headers, minNetworkTimeout, progressListener, HTTP_REQUEST_MAX_ATTEMPTS, 0);
+ }
+ }
+
+ private HttpURLConnection getConnection(Context context, String url, List<String> parameterNames, List<Object> parameterValues, Map<String, String> headers, int minNetworkTimeout, ProgressListener progressListener, int retriesLeft, int attempts) throws Exception {
+ SharedPreferences prefs = Util.getPreferences(context);
+ int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, SOCKET_READ_TIMEOUT_DEFAULT + ""));
+ minNetworkTimeout = Math.max(minNetworkTimeout, networkTimeout);
+ attempts++;
+ retriesLeft--;
+
+ try {
+ return getConnectionDirect(context, url, parameterNames, parameterValues, headers, minNetworkTimeout);
+ } catch (IOException x) {
+ if(retriesLeft > 0) {
+ if (progressListener != null) {
+ String msg = context.getResources().getString(R.string.music_service_retry, attempts, HTTP_REQUEST_MAX_ATTEMPTS - 1);
+ progressListener.updateProgress(msg);
+ }
- private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,
- List<Object> parameterValues, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
- return getResponseForURL(context, url, requestParams, parameterNames, parameterValues, null, progressListener, task, false).getEntity();
+ Log.w(TAG, "Got IOException " + x + " (" + attempts + "), will retry");
+ Thread.sleep(2000L);
+
+ minNetworkTimeout = (int) (minNetworkTimeout * 1.3);
+ return getConnection(context, url, parameterNames, parameterValues, headers, minNetworkTimeout, progressListener, retriesLeft, attempts);
+ } else {
+ throw x;
+ }
+ }
}
- private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames,
- List<Object> parameterValues, ProgressListener progressListener, SilentBackgroundTask task, boolean throwsError) throws Exception {
- return getResponseForURL(context, url, requestParams, parameterNames, parameterValues, null, progressListener, task, throwsError).getEntity();
- }
- private HttpResponse getResponseForURL(Context context, String url, HttpParams requestParams,
- List<String> parameterNames, List<Object> parameterValues,
- List<Header> headers, ProgressListener progressListener, SilentBackgroundTask task, boolean throwsErrors) throws Exception {
- // If not too many parameters, extract them to the URL rather than relying on the HTTP POST request being
- // received intact. Remember, HTTP POST requests are converted to GET requests during HTTP redirects, thus
- // loosing its entity.
- if (parameterNames != null && parameterNames.size() < 10) {
- StringBuilder builder = new StringBuilder(url);
- for (int i = 0; i < parameterNames.size(); i++) {
- builder.append("&").append(parameterNames.get(i)).append("=");
+ private HttpURLConnection getConnectionDirect(Context context, String url, List<String> parameterNames, List<Object> parameterValues, Map<String, String> headers, int minNetworkTimeout) throws Exception {
+ // Add params to query
+ if (parameterNames != null) {
+ StringBuilder builder = new StringBuilder(url);
+ for (int i = 0; i < parameterNames.size(); i++) {
+ builder.append("&").append(parameterNames.get(i)).append("=");
String part = URLEncoder.encode(String.valueOf(parameterValues.get(i)), "UTF-8");
part = part.replaceAll("\\%27", "'");
- builder.append(part);
- }
- url = builder.toString();
- parameterNames = null;
- parameterValues = null;
- }
+ builder.append(part);
+ }
+ url = builder.toString();
+ }
- String rewrittenUrl = rewriteUrlWithRedirect(context, url);
- return executeWithRetry(context, rewrittenUrl, url, requestParams, parameterNames, parameterValues, headers, progressListener, task, throwsErrors);
- }
+ // Rewrite url based on redirects
+ String rewrittenUrl = rewriteUrlWithRedirect(context, url);
+ if(rewrittenUrl.indexOf("scanstatus") == -1) {
+ Log.i(TAG, stripUrlInfo(rewrittenUrl));
+ }
- private HttpResponse executeWithRetry(final Context context, String url, String originalUrl, HttpParams requestParams,
- List<String> parameterNames, List<Object> parameterValues,
- List<Header> headers, ProgressListener progressListener, SilentBackgroundTask task, boolean throwErrors) throws Exception {
- // Strip out sensitive information from log
- if(url.indexOf("scanstatus") == -1) {
- Log.i(TAG, stripUrlInfo(url));
+ return getConnectionDirect(context, rewrittenUrl, headers, minNetworkTimeout);
+ }
+
+ private HttpURLConnection getConnectionDirect(Context context, String url, Map<String, String> headers, int minNetworkTimeout) throws Exception {
+ if(!hasInstalledGoogleSSL) {
+ try {
+ ProviderInstaller.installIfNeeded(context);
+ } catch(Exception e) {
+ // Just continue on anyways, doesn't really harm anything if this fails
+ Log.w(TAG, "Failed to update to use Google Play SSL", e);
+ }
+ hasInstalledGoogleSSL = true;
}
- SharedPreferences prefs = Util.getPreferences(context);
- int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, "15000"));
- HttpParams newParams = httpClient.getParams();
- HttpConnectionParams.setSoTimeout(newParams, networkTimeout);
- httpClient.setParams(newParams);
-
- final AtomicReference<Boolean> isCancelled = new AtomicReference<Boolean>(false);
- int attempts = 0;
- while (true) {
- attempts++;
- HttpContext httpContext = new BasicHttpContext();
- final HttpRequestBase request = (url.indexOf("rest") == -1) ? new HttpGet(url) : new HttpPost(url);
-
- if (task != null) {
- // Attempt to abort the HTTP request if the task is cancelled.
- task.setOnCancelListener(new BackgroundTask.OnCancelListener() {
- @Override
- public void onCancel() {
- try {
- isCancelled.set(true);
- if(Thread.currentThread() == Looper.getMainLooper().getThread()) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- request.abort();
- return null;
- }
- }.execute();
- } else {
- request.abort();
- }
- } catch(Exception e) {
- Log.e(TAG, "Failed to stop http task", e);
- }
- }
- });
- }
-
- if (parameterNames != null && request instanceof HttpPost) {
- List<NameValuePair> params = new ArrayList<NameValuePair>();
- for (int i = 0; i < parameterNames.size(); i++) {
- params.add(new BasicNameValuePair(parameterNames.get(i), String.valueOf(parameterValues.get(i))));
- }
- ((HttpPost) request).setEntity(new UrlEncodedFormEntity(params, Constants.UTF_8));
- }
-
- if (requestParams != null) {
- request.setParams(requestParams);
- }
-
- if (headers != null) {
- for (Header header : headers) {
- request.addHeader(header);
- }
- }
- if(url.indexOf("getCoverArt") == -1 && url.indexOf("stream") == -1 && url.indexOf("getAvatar") == -1) {
- request.addHeader("Accept-Encoding", "gzip");
+ // Connect and add headers
+ URL urlObj = new URL(url);
+ HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
+ if(url.indexOf("getCoverArt") == -1 && url.indexOf("stream") == -1 && url.indexOf("getAvatar") == -1) {
+ connection.addRequestProperty("Accept-Encoding", "gzip");
+ }
+ connection.addRequestProperty("User-Agent", Constants.REST_CLIENT_ID);
+
+ // Set timeout
+ connection.setConnectTimeout(minNetworkTimeout);
+ connection.setReadTimeout(minNetworkTimeout);
+
+ // Add headers
+ if(headers != null) {
+ for(Map.Entry<String, String> header: headers.entrySet()) {
+ connection.setRequestProperty(header.getKey(), header.getValue());
}
- request.addHeader("User-Agent", Constants.REST_CLIENT_ID);
-
- // Set credentials to get through apache proxies that require authentication.
- int instance = getInstance(context);
- String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
- String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null);
- httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
- new UsernamePasswordCredentials(username, password));
-
- try {
- HttpResponse response = httpClient.execute(request, httpContext);
- detectRedirect(originalUrl, context, httpContext);
- return response;
- } catch (IOException x) {
- request.abort();
- if (attempts >= HTTP_REQUEST_MAX_ATTEMPTS || isCancelled.get() || throwErrors) {
- throw x;
- }
- if (progressListener != null) {
- String msg = context.getResources().getString(R.string.music_service_retry, attempts, HTTP_REQUEST_MAX_ATTEMPTS - 1);
- progressListener.updateProgress(msg);
- }
- Log.w(TAG, "Got IOException " + x + " (" + attempts + "), will retry");
- increaseTimeouts(requestParams);
- Thread.sleep(2000L);
- }
- }
- }
+ }
- private void increaseTimeouts(HttpParams requestParams) {
- if (requestParams != null) {
- int connectTimeout = HttpConnectionParams.getConnectionTimeout(requestParams);
- if (connectTimeout != 0) {
- HttpConnectionParams.setConnectionTimeout(requestParams, (int) (connectTimeout * 1.3F));
- }
- int readTimeout = HttpConnectionParams.getSoTimeout(requestParams);
- if (readTimeout != 0) {
- HttpConnectionParams.setSoTimeout(requestParams, (int) (readTimeout * 1.5F));
- }
- }
- }
+ if(connection instanceof HttpsURLConnection) {
+ HttpsURLConnection sslConnection = (HttpsURLConnection) connection;
+ sslConnection.setSSLSocketFactory(sslSocketFactory);
+ sslConnection.setHostnameVerifier(selfSignedHostnameVerifier);
+ }
- private void detectRedirect(String originalUrl, Context context, HttpContext httpContext) throws Exception {
- HttpUriRequest request = (HttpUriRequest) httpContext.getAttribute(ExecutionContext.HTTP_REQUEST);
- HttpHost host = (HttpHost) httpContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
-
- // Sometimes the request doesn't contain the "http://host" part
- String redirectedUrl;
- if (request.getURI().getScheme() == null) {
- redirectedUrl = host.toURI() + request.getURI();
- } else {
- redirectedUrl = request.getURI().toString();
+ // Force the connection to initiate
+ if(connection.getResponseCode() >= 500) {
+ throw new IOException("Error code: " + connection.getResponseCode());
+ }
+ if(detectRedirect(context, urlObj, connection)) {
+ String rewrittenUrl = rewriteUrlWithRedirect(context, url);
+ if(!rewrittenUrl.equals(url)) {
+ connection.disconnect();
+ return getConnectionDirect(context, rewrittenUrl, headers, minNetworkTimeout);
+ }
}
+ return connection;
+ }
+
+ // Returns true when we should immediately retry with the redirect
+ private boolean detectRedirect(Context context, URL originalUrl, HttpURLConnection connection) throws Exception {
+ if(connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP || connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM) {
+ String redirectLocation = connection.getHeaderField("Location");
+ if(redirectLocation != null) {
+ detectRedirect(context, originalUrl.toExternalForm(), redirectLocation);
+ return true;
+ }
+ }
+
+ detectRedirect(context, originalUrl, connection.getURL());
+ return false;
+ }
+ private void detectRedirect(Context context, URL originalUrl, URL redirectedUrl) throws Exception {
+ detectRedirect(context, originalUrl.toExternalForm(), redirectedUrl.toExternalForm());
+ }
+ private void detectRedirect(Context context, String originalUrl, String redirectedUrl) throws Exception {
if(redirectedUrl != null && "http://subsonic.org/pages/".equals(redirectedUrl)) {
throw new Exception("Invalid url, redirects to http://subsonic.org/pages/");
}
@@ -2109,7 +1945,7 @@ public class RESTMusicService implements MusicService {
redirectionLastChecked = System.currentTimeMillis();
redirectionNetworkType = getCurrentNetworkType(context);
}
- }
+ }
private String rewriteUrlWithRedirect(Context context, String url) {
@@ -2158,7 +1994,10 @@ public class RESTMusicService implements MusicService {
}
}
- public HttpClient getHttpClient() {
- return httpClient;
+ public SSLSocketFactory getSSLSocketFactory() {
+ return sslSocketFactory;
+ }
+ public HostnameVerifier getHostNameVerifier() {
+ return selfSignedHostnameVerifier;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java b/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
index 6c70496d..617144d7 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
@@ -19,20 +19,32 @@
package github.daneren2005.dsub.service;
+import android.content.SharedPreferences;
import android.util.Log;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;
+import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.RemoteStatus;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.Util;
+import github.daneren2005.serverproxy.FileProxy;
+import github.daneren2005.serverproxy.ServerProxy;
import github.daneren2005.serverproxy.WebProxy;
public abstract class RemoteController {
private static final String TAG = RemoteController.class.getSimpleName();
protected DownloadService downloadService;
protected boolean nextSupported = false;
+ protected ServerProxy proxy;
+ protected String rootLocation = "";
+
+ public RemoteController(DownloadService downloadService) {
+ this.downloadService = downloadService;
+ SharedPreferences prefs = Util.getPreferences(downloadService);
+ rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
+ }
public abstract void create(boolean playing, int seconds);
public abstract void start();
@@ -105,9 +117,62 @@ public abstract class RemoteController {
protected WebProxy createWebProxy() {
MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
if(musicService instanceof CachedMusicService) {
- return new WebProxy(downloadService, ((CachedMusicService)musicService).getMusicService().getHttpClient());
+ RESTMusicService restMusicService = ((CachedMusicService)musicService).getMusicService();
+ return new WebProxy(downloadService, restMusicService.getSSLSocketFactory(), restMusicService.getHostNameVerifier());
} else {
return new WebProxy(downloadService);
}
}
+
+ protected String getStreamUrl(MusicService musicService, DownloadFile downloadFile) throws Exception {
+ MusicDirectory.Entry song = downloadFile.getSong();
+
+ String url;
+ // In offline mode or playing offline song
+ if(downloadFile.isStream()) {
+ url = downloadFile.getStream();
+ } else if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
+ if(proxy == null) {
+ proxy = new FileProxy(downloadService);
+ proxy.start();
+ }
+
+ // Offline song
+ if(song.getId().indexOf(rootLocation) != -1) {
+ url = proxy.getPublicAddress(song.getId());
+ } else {
+ // Playing online song in offline mode
+ url = proxy.getPublicAddress(downloadFile.getCompleteFile().getPath());
+ }
+ } else {
+ // Check if we want a proxy going still
+ if(Util.isCastProxy(downloadService)) {
+ if(proxy instanceof FileProxy) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(proxy == null) {
+ proxy = createWebProxy();
+ proxy.start();
+ }
+ } else if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(song.isVideo()) {
+ url = musicService.getHlsUrl(song.getId(), downloadFile.getBitRate(), downloadService);
+ } else {
+ url = musicService.getMusicUrl(downloadService, song, downloadFile.getBitRate());
+ }
+
+ // If proxy is going, it is a WebProxy
+ if(proxy != null) {
+ url = proxy.getPublicAddress(url);
+ }
+ }
+
+ return url;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java
index afb05928..1b389f80 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/ErrorParser.java
@@ -27,7 +27,6 @@ import java.io.Reader;
* @author Sindre Mehus
*/
public class ErrorParser extends AbstractParser {
-
public ErrorParser(Context context, int instance) {
super(context, instance);
}
@@ -45,5 +44,6 @@ public class ErrorParser extends AbstractParser {
} while (eventType != XmlPullParser.END_DOCUMENT);
validate();
+ reader.close();
}
} \ No newline at end of file
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java
deleted file mode 100644
index 830950c8..00000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/SSLSocketFactory.java
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package github.daneren2005.dsub.service.ssl;
-
-import android.os.Build;
-import android.util.Log;
-
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.conn.scheme.HostNameResolver;
-import org.apache.http.conn.scheme.LayeredSocketFactory;
-import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
-import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
-import org.apache.http.conn.ssl.StrictHostnameVerifier;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-
-import java.io.IOException;
-import java.lang.reflect.Array;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Provider;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.UnrecoverableKeyException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Layered socket factory for TLS/SSL connections.
- * <p>
- * SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of
- * trusted certificates and to authenticate to the HTTPS server using a private key.
- * <p>
- * SSLSocketFactory will enable server authentication when supplied with
- * a {@link KeyStore trust-store} file containing one or several trusted certificates. The client
- * secure socket will reject the connection during the SSL session handshake if the target HTTPS
- * server attempts to authenticate itself with a non-trusted certificate.
- * <p>
- * Use JDK keytool utility to import a trusted certificate and generate a trust-store file:
- * <pre>
- * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
- * </pre>
- * <p>
- * In special cases the standard trust verification process can be bypassed by using a custom
- * {@link TrustStrategy}. This interface is primarily intended for allowing self-signed
- * certificates to be accepted as trusted without having to add them to the trust-store file.
- * <p>
- * The following parameters can be used to customize the behavior of this
- * class:
- * <ul>
- * <li>{@link org.apache.http.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li>
- * <li>{@link org.apache.http.params.CoreConnectionPNames#SO_TIMEOUT}</li>
- * </ul>
- * <p>
- * SSLSocketFactory will enable client authentication when supplied with
- * a {@link KeyStore key-store} file containing a private key/public certificate
- * pair. The client secure socket will use the private key to authenticate
- * itself to the target HTTPS server during the SSL session handshake if
- * requested to do so by the server.
- * The target HTTPS server will in its turn verify the certificate presented
- * by the client in order to establish client's authenticity
- * <p>
- * Use the following sequence of actions to generate a key-store file
- * </p>
- * <ul>
- * <li>
- * <p>
- * Use JDK keytool utility to generate a new key
- * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
- * For simplicity use the same password for the key as that of the key-store
- * </p>
- * </li>
- * <li>
- * <p>
- * Issue a certificate signing request (CSR)
- * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
- * </p>
- * </li>
- * <li>
- * <p>
- * Send the certificate request to the trusted Certificate Authority for signature.
- * One may choose to act as her own CA and sign the certificate request using a PKI
- * tool, such as OpenSSL.
- * </p>
- * </li>
- * <li>
- * <p>
- * Import the trusted CA root certificate
- * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
- * </p>
- * </li>
- * <li>
- * <p>
- * Import the PKCS#7 file containg the complete certificate chain
- * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
- * </p>
- * </li>
- * <li>
- * <p>
- * Verify the content the resultant keystore file
- * <pre>keytool -list -v -keystore my.keystore</pre>
- * </p>
- * </li>
- * </ul>
- *
- * @since 4.0
- */
-public class SSLSocketFactory implements LayeredSocketFactory {
- private static final String TAG = SSLSocketFactory.class.getSimpleName();
- public static final String TLS = "TLS";
-
- public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
- = new AllowAllHostnameVerifier();
-
- public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
- = new BrowserCompatHostnameVerifier();
-
- public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
- = new StrictHostnameVerifier();
-
- /**
- * The default factory using the default JVM settings for secure connections.
- */
- private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
-
- /**
- * Gets the default factory, which uses the default JVM settings for secure
- * connections.
- *
- * @return the default factory
- */
- public static SSLSocketFactory getSocketFactory() {
- return DEFAULT_FACTORY;
- }
-
- private final javax.net.ssl.SSLSocketFactory socketfactory;
- private final HostNameResolver nameResolver;
- // TODO: make final
- private volatile X509HostnameVerifier hostnameVerifier;
-
- private static SSLContext createSSLContext(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final TrustStrategy trustStrategy)
- throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException {
- if (algorithm == null) {
- algorithm = TLS;
- }
- KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
- KeyManagerFactory.getDefaultAlgorithm());
- kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null);
- KeyManager[] keymanagers = kmfactory.getKeyManagers();
- TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- tmfactory.init(keystore);
- TrustManager[] trustmanagers = tmfactory.getTrustManagers();
- if (trustmanagers != null && trustStrategy != null) {
- for (int i = 0; i < trustmanagers.length; i++) {
- TrustManager tm = trustmanagers[i];
- if (tm instanceof X509TrustManager) {
- trustmanagers[i] = new TrustManagerDecorator(
- (X509TrustManager) tm, trustStrategy);
- }
- }
- }
-
- SSLContext sslcontext = SSLContext.getInstance(algorithm);
- sslcontext.init(keymanagers, trustmanagers, random);
- return sslcontext;
- }
-
- /**
- * @deprecated Use {@link #SSLSocketFactory(String, KeyStore, String, KeyStore, SecureRandom, X509HostnameVerifier)}
- */
- @Deprecated
- public SSLSocketFactory(
- final String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final HostNameResolver nameResolver)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, null),
- nameResolver);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, null),
- hostnameVerifier);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final TrustStrategy trustStrategy,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, trustStrategy),
- hostnameVerifier);
- }
-
- public SSLSocketFactory(
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, keystore, keystorePassword, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(
- final KeyStore keystore,
- final String keystorePassword)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{
- this(TLS, keystore, keystorePassword, null, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(
- final KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final TrustStrategy trustStrategy,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, null, null, trustStrategy, hostnameVerifier);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final TrustStrategy trustStrategy)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, null, null, trustStrategy, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(final SSLContext sslContext) {
- this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- /**
- * @deprecated Use {@link #SSLSocketFactory(SSLContext)}
- */
- @Deprecated
- public SSLSocketFactory(
- final SSLContext sslContext, final HostNameResolver nameResolver) {
- super();
- this.socketfactory = sslContext.getSocketFactory();
- this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
- this.nameResolver = nameResolver;
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
- super();
- this.socketfactory = sslContext.getSocketFactory();
- this.hostnameVerifier = hostnameVerifier;
- this.nameResolver = null;
- }
-
- private SSLSocketFactory() {
- super();
- this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
- this.hostnameVerifier = null;
- this.nameResolver = null;
- }
-
- /**
- * @param params Optional parameters. Parameters passed to this method will have no effect.
- * This method will create a unconnected instance of {@link Socket} class
- * using {@link javax.net.ssl.SSLSocketFactory#createSocket()} method.
- * @since 4.1
- */
- @SuppressWarnings("cast")
- public Socket createSocket(final HttpParams params) throws IOException {
- // the cast makes sure that the factory is working as expected
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket();
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- return sslSocket;
- }
-
- @SuppressWarnings("cast")
- public Socket createSocket() throws IOException {
- // the cast makes sure that the factory is working as expected
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket();
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- return sslSocket;
- }
-
- /**
- * @since 4.1
- */
- public Socket connectSocket(
- final Socket sock,
- final InetSocketAddress remoteAddress,
- final InetSocketAddress localAddress,
- final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null");
- }
- if (params == null) {
- throw new IllegalArgumentException("HTTP parameters may not be null");
- }
- SSLSocket sslsock = (SSLSocket) (sock != null ? sock : createSocket());
- if (localAddress != null) {
-// sslsock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
- sslsock.bind(localAddress);
- }
-
- setHostName(sslsock, remoteAddress.getHostName());
- int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
- int soTimeout = HttpConnectionParams.getSoTimeout(params);
-
- try {
- sslsock.connect(remoteAddress, connTimeout);
- } catch (SocketTimeoutException ex) {
- throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
- + remoteAddress.getAddress() + " timed out");
- }
- sslsock.setSoTimeout(soTimeout);
- if (this.hostnameVerifier != null) {
- try {
- this.hostnameVerifier.verify(remoteAddress.getHostName(), sslsock);
- // verifyHostName() didn't blowup - good!
- } catch (IOException iox) {
- // close the socket before re-throwing the exception
- try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
- throw iox;
- }
- }
- return sslsock;
- }
-
-
- /**
- * Checks whether a socket connection is secure.
- * This factory creates TLS/SSL socket connections
- * which, by default, are considered secure.
- * <br/>
- * Derived classes may override this method to perform
- * runtime checks, for example based on the cypher suite.
- *
- * @param sock the connected socket
- *
- * @return <code>true</code>
- *
- * @throws IllegalArgumentException if the argument is invalid
- */
- public boolean isSecure(final Socket sock) throws IllegalArgumentException {
- if (sock == null) {
- throw new IllegalArgumentException("Socket may not be null");
- }
- // This instanceof check is in line with createSocket() above.
- if (!(sock instanceof SSLSocket)) {
- throw new IllegalArgumentException("Socket not created by this factory");
- }
- // This check is performed last since it calls the argument object.
- if (sock.isClosed()) {
- throw new IllegalArgumentException("Socket is closed");
- }
- return true;
- }
-
- /**
- * @since 4.1
- */
- public Socket createLayeredSocket(
- final Socket socket,
- final String host,
- final int port,
- final boolean autoClose) throws IOException, UnknownHostException {
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
- socket,
- host,
- port,
- autoClose
- );
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- if (this.hostnameVerifier != null) {
- this.hostnameVerifier.verify(host, sslSocket);
- }
- // verifyHostName() didn't blowup - good!
- return sslSocket;
- }
-
- @Deprecated
- public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
- if ( hostnameVerifier == null ) {
- throw new IllegalArgumentException("Hostname verifier may not be null");
- }
- this.hostnameVerifier = hostnameVerifier;
- }
-
- public X509HostnameVerifier getHostnameVerifier() {
- return this.hostnameVerifier;
- }
-
- /**
- * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}
- */
- @Deprecated
- public Socket connectSocket(
- final Socket socket,
- final String host, int port,
- final InetAddress localAddress, int localPort,
- final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
- InetSocketAddress local = null;
- if (localAddress != null || localPort > 0) {
- // we need to bind explicitly
- if (localPort < 0) {
- localPort = 0; // indicates "any"
- }
- local = new InetSocketAddress(localAddress, localPort);
- }
- InetAddress remoteAddress;
- if (this.nameResolver != null) {
- remoteAddress = this.nameResolver.resolve(host);
- } else {
- remoteAddress = InetAddress.getByName(host);
- }
- InetSocketAddress remote = new InetSocketAddress(remoteAddress, port);
- return connectSocket(socket, remote, local, params);
- }
-
- /**
- * @deprecated Use {@link #createLayeredSocket(Socket, String, int, boolean)}
- */
- @Deprecated
- public Socket createSocket(
- final Socket socket,
- final String host, int port,
- boolean autoClose) throws IOException, UnknownHostException {
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(socket, host, port, autoClose);
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- setHostName(sslSocket, host);
- return sslSocket;
- }
-
- private void setHostName(SSLSocket sslsock, String hostname){
- try {
- java.lang.reflect.Method setHostnameMethod = sslsock.getClass().getMethod("setHostname", String.class);
- setHostnameMethod.invoke(sslsock, hostname);
- } catch (Exception e) {
- Log.w(TAG, "SNI not useable", e);
- }
- }
-
- private String[] getProtocols(SSLSocket sslSocket) {
- String[] protocols = sslSocket.getEnabledProtocols();
-
- // Remove SSLv3 if it is not the only option
- if(protocols.length > 1) {
- List<String> protocolList = new ArrayList(Arrays.asList(protocols));
- protocolList.remove("SSLv3");
- protocols = protocolList.toArray(new String[protocolList.size()]);
- }
-
- return protocols;
- }
-
- private String[] getCiphers(SSLSocket sslSocket) {
- String[] ciphers = sslSocket.getEnabledCipherSuites();
-
- List<String> enabledCiphers = new ArrayList(Arrays.asList(ciphers));
- // On Android 5.0 release, Jetty doesn't seem to play nice with these ciphers
- // Issue seems to have been fixed in M, and now won't work without them. Because Google
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- enabledCiphers.remove("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
- enabledCiphers.remove("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
- }
-
- ciphers = enabledCiphers.toArray(new String[enabledCiphers.size()]);
- return ciphers;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
deleted file mode 100644
index f2364368..00000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package github.daneren2005.dsub.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.X509TrustManager;
-
-
-/**
- * @since 4.1
- */
-class TrustManagerDecorator implements X509TrustManager {
-
- private final X509TrustManager trustManager;
- private final TrustStrategy trustStrategy;
-
- TrustManagerDecorator(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
- super();
- this.trustManager = trustManager;
- this.trustStrategy = trustStrategy;
- }
-
- public void checkClientTrusted(
- final X509Certificate[] chain, final String authType) throws CertificateException {
- this.trustManager.checkClientTrusted(chain, authType);
- }
-
- public void checkServerTrusted(
- final X509Certificate[] chain, final String authType) throws CertificateException {
- if (!this.trustStrategy.isTrusted(chain, authType)) {
- this.trustManager.checkServerTrusted(chain, authType);
- }
- }
-
- public X509Certificate[] getAcceptedIssuers() {
- return this.trustManager.getAcceptedIssuers();
- }
-
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
deleted file mode 100644
index 637a8931..00000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package github.daneren2005.dsub.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-/**
- * A trust strategy that accepts self-signed certificates as trusted. Verification of all other
- * certificates is done by the trust manager configured in the SSL context.
- *
- * @since 4.1
- */
-public class TrustSelfSignedStrategy implements TrustStrategy {
-
- public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
- return true;
- }
-
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java
deleted file mode 100644
index 334a97c5..00000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package github.daneren2005.dsub.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-/**
- * A strategy to establish trustworthiness of certificates without consulting the trust manager
- * configured in the actual SSL context. This interface can be used to override the standard
- * JSSE certificate verification process.
- *
- * @since 4.1
- */
-public interface TrustStrategy {
-
- /**
- * Determines whether the certificate chain can be trusted without consulting the trust manager
- * configured in the actual SSL context. This method can be used to override the standard JSSE
- * certificate verification process.
- * <p>
- * Please note that, if this method returns <code>false</code>, the trust manager configured
- * in the actual SSL context can still clear the certificate as trusted.
- *
- * @param chain the peer certificate chain
- * @param authType the authentication type based on the client certificate
- * @return <code>true</code> if the certificate can be trusted without verification by
- * the trust manager, <code>false</code> otherwise.
- * @throws CertificateException thrown if the certificate is not trusted or invalid.
- */
- boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException;
-
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
index cc8e241d..f03906a8 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
@@ -95,7 +95,7 @@ public class DrawableTint {
return getTintedDrawable(context, drawableRes, colorAttr);
}
- public static void wipeTintCache() {
+ public static void clearCache() {
attrMap.clear();
tintedDrawables.clear();
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java b/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java
index 90c885f6..1309ee69 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java
@@ -120,7 +120,6 @@ public class SongDBHandler extends SQLiteOpenHelper {
values.put(SONGS_SERVER_KEY, serverKey);
values.put(SONGS_SERVER_ID, entry.getFirst());
values.put(SONGS_COMPLETE_PATH, entry.getSecond());
- // Util.sleepQuietly(10000);
db.insertWithOnConflict(TABLE_SONGS, null, values, SQLiteDatabase.CONFLICT_IGNORE);
}
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 b69ea55e..7f8a168d 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
@@ -19,7 +19,6 @@ package github.daneren2005.dsub.util;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.graphics.Color;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.content.ClipboardManager;
@@ -29,7 +28,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -50,15 +48,11 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.Gravity;
-import android.view.Window;
-import android.view.WindowManager;
import android.widget.AdapterView;
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;
@@ -67,8 +61,6 @@ import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
import github.daneren2005.dsub.service.DownloadService;
-import org.apache.http.HttpEntity;
-
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
@@ -80,7 +72,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.text.NumberFormat;
@@ -603,13 +594,6 @@ public final class Util {
}
}
- public static String getContentType(HttpEntity entity) {
- if (entity == null || entity.getContentType() == null) {
- return null;
- }
- return entity.getContentType().getValue();
- }
-
public static int getRemainingTrialDays(Context context) {
SharedPreferences prefs = getPreferences(context);
long installTime = prefs.getLong(Constants.PREFERENCES_KEY_INSTALL_TIME, 0L);
diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java
index 1f7035dc..4f9a27f0 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java
@@ -29,7 +29,7 @@ public abstract class RemoteControlClientBase {
public abstract void register(final Context context, final ComponentName mediaButtonReceiverComponent);
public abstract void unregister(final Context context);
- public abstract void setPlaybackState(int state);
+ public abstract void setPlaybackState(int state, int index, int queueSize);
public abstract void updateMetadata(Context context, MusicDirectory.Entry currentSong);
public abstract void metadataChanged(MusicDirectory.Entry currentSong);
public abstract void updateAlbumArt(MusicDirectory.Entry currentSong, Bitmap bitmap);
diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
index 2a06e798..74076afb 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
@@ -54,7 +54,7 @@ public class RemoteControlClientICS extends RemoteControlClientBase {
audioManager.unregisterRemoteControlClient(mRemoteControl);
}
- public void setPlaybackState(final int state) {
+ public void setPlaybackState(final int state, int index, int queueSize) {
if(mRemoteControl == null) {
return;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java
index e61e9a47..d10c8594 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientJB.java
@@ -1,17 +1,10 @@
package github.daneren2005.dsub.util.compat;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.media.MediaMetadataRetriever;
import android.media.RemoteControlClient;
-import github.daneren2005.dsub.activity.SubsonicActivity;
-import github.daneren2005.dsub.service.DownloadService;
+
import github.daneren2005.dsub.util.SilentBackgroundTask;
@TargetApi(18)
@@ -36,13 +29,13 @@ public class RemoteControlClientJB extends RemoteControlClientICS {
return null;
}
}.execute();
- setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
+ setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING, 0, 0);
}
});
}
@Override
- public void setPlaybackState(final int state) {
+ public void setPlaybackState(final int state, int index, int queueSize) {
if(mRemoteControl == null) {
return;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java
index df468155..d666afb2 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java
@@ -39,7 +39,6 @@ import android.support.v7.media.MediaRouter;
import android.util.Log;
import android.view.KeyEvent;
-import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -50,7 +49,6 @@ import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.MusicDirectory.Entry;
import github.daneren2005.dsub.domain.Playlist;
-import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.domain.SearchCritera;
import github.daneren2005.dsub.domain.SearchResult;
import github.daneren2005.dsub.service.DownloadFile;
@@ -123,8 +121,12 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
mediaSession.release();
}
+ private void setPlaybackState(int state) {
+ setPlaybackState(state, downloadService.getCurrentPlayingIndex(), downloadService.size());
+ }
+
@Override
- public void setPlaybackState(int state) {
+ public void setPlaybackState(int state, int index, int queueSize) {
PlaybackState.Builder builder = new PlaybackState.Builder();
int newState = PlaybackState.STATE_NONE;
@@ -156,7 +158,7 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
isSong = entry.isSong();
}
- builder.setActions(getPlaybackActions(isSong));
+ builder.setActions(getPlaybackActions(isSong, index, queueSize));
if(entry != null) {
addCustomActions(entry, builder);
@@ -240,14 +242,12 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
return mediaSession;
}
- protected long getPlaybackActions(boolean isSong) {
+ protected long getPlaybackActions(boolean isSong, int currentIndex, int size) {
long actions = PlaybackState.ACTION_PLAY |
PlaybackState.ACTION_PAUSE |
PlaybackState.ACTION_SEEK_TO |
PlaybackState.ACTION_SKIP_TO_QUEUE_ITEM;
- int currentIndex = downloadService.getCurrentPlayingIndex();
- int size = downloadService.size();
if(isSong) {
if (currentIndex > 0) {
actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
diff --git a/app/src/main/res/layout-land/download.xml b/app/src/main/res/layout-land/download.xml
index 894ae62e..855bf2a9 100644
--- a/app/src/main/res/layout-land/download.xml
+++ b/app/src/main/res/layout-land/download.xml
@@ -37,7 +37,7 @@
android:layout_centerHorizontal="true"
android:layout_above="@+id/download_song_title">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:id="@+id/download_other_controls_layout"
android:orientation="horizontal"
android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/abstract_fragment_activity.xml b/app/src/main/res/layout/abstract_fragment_activity.xml
index 462ee7ef..ae6647c4 100644
--- a/app/src/main/res/layout/abstract_fragment_activity.xml
+++ b/app/src/main/res/layout/abstract_fragment_activity.xml
@@ -137,7 +137,7 @@
</LinearLayout>
</FrameLayout>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <FrameLayout
android:id="@+id/now_playing_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
diff --git a/app/src/main/res/layout/change_email.xml b/app/src/main/res/layout/change_email.xml
index 87d297be..d78edd13 100644
--- a/app/src/main/res/layout/change_email.xml
+++ b/app/src/main/res/layout/change_email.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/confirm_password.xml b/app/src/main/res/layout/confirm_password.xml
index 9ec61c0a..d74eecfd 100644
--- a/app/src/main/res/layout/confirm_password.xml
+++ b/app/src/main/res/layout/confirm_password.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/create_bookmark.xml b/app/src/main/res/layout/create_bookmark.xml
index d6f077c3..22d96227 100644
--- a/app/src/main/res/layout/create_bookmark.xml
+++ b/app/src/main/res/layout/create_bookmark.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/create_podcast.xml b/app/src/main/res/layout/create_podcast.xml
index 04e74ec3..a5e66792 100644
--- a/app/src/main/res/layout/create_podcast.xml
+++ b/app/src/main/res/layout/create_podcast.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/create_user.xml b/app/src/main/res/layout/create_user.xml
index b2d8f6e0..7d77ade9 100644
--- a/app/src/main/res/layout/create_user.xml
+++ b/app/src/main/res/layout/create_user.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -26,7 +26,7 @@
android:textColor="?android:textColorPrimary"/>
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -48,7 +48,7 @@
android:textColor="?android:textColorPrimary"/>
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/notification.xml b/app/src/main/res/layout/notification.xml
index 4a89db49..0ab5a884 100644
--- a/app/src/main/res/layout/notification.xml
+++ b/app/src/main/res/layout/notification.xml
@@ -13,7 +13,6 @@
android:gravity="center" />
<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/app/src/main/res/layout/notification_expanded.xml b/app/src/main/res/layout/notification_expanded.xml
index 7b378e12..a1586214 100644
--- a/app/src/main/res/layout/notification_expanded.xml
+++ b/app/src/main/res/layout/notification_expanded.xml
@@ -12,7 +12,6 @@
android:gravity="center" />
<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="0.0"
diff --git a/app/src/main/res/layout/select_album_header.xml b/app/src/main/res/layout/select_album_header.xml
index 5b2294f0..891db891 100644
--- a/app/src/main/res/layout/select_album_header.xml
+++ b/app/src/main/res/layout/select_album_header.xml
@@ -121,7 +121,7 @@
android:contentDescription="@null"/>
</RelativeLayout>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <FrameLayout
android:id="@+id/header_progress"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
diff --git a/app/src/main/res/layout/shuffle_dialog.xml b/app/src/main/res/layout/shuffle_dialog.xml
index 63778ed7..012c220f 100644
--- a/app/src/main/res/layout/shuffle_dialog.xml
+++ b/app/src/main/res/layout/shuffle_dialog.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -26,7 +26,7 @@
android:hint="@string/shuffle.startYear" />
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -49,7 +49,7 @@
android:hint="@string/shuffle.endYear" />
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/update_playlist.xml b/app/src/main/res/layout/update_playlist.xml
index cc7e5ee6..f9cc6a90 100644
--- a/app/src/main/res/layout/update_playlist.xml
+++ b/app/src/main/res/layout/update_playlist.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -26,7 +26,7 @@
android:textColor="?android:textColorPrimary"/>
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -49,7 +49,7 @@
android:hint="@string/common.comment" />
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/update_share.xml b/app/src/main/res/layout/update_share.xml
index ef44e304..0d06e00d 100644
--- a/app/src/main/res/layout/update_share.xml
+++ b/app/src/main/res/layout/update_share.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -26,7 +26,7 @@
android:hint="@string/common.name" />
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -48,7 +48,7 @@
android:calendarViewShown="false"/>
</LinearLayout>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9ac609c3..0d9c2aa1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -103,7 +103,7 @@
<string name="menu.add_podcast">Add Channel</string>
<string name="menu.keep_synced">Keep Synced</string>
<string name="menu.stop_sync">Stop syncing</string>
- <string name="menu.show_all">Show all media</string>
+ <string name="menu.show_all">Show all songs</string>
<string name="menu.show_artist">Show Artist</string>
<string name="menu.share">Share</string>
<string name="menu.delete_cache">Delete Cache</string>
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index 169edaa0..dc77a5a3 100644
--- a/app/src/main/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
@@ -1,5 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
+ <release version="5.3.2" versioncode="190" releasedate="10/22/2016">
+ <change>Add support for casting Internet Radio to ChromeCast/DLNA</change>
+ <change>Add support for Play Title by Artist from Google Search</change>
+ <change>Move to more modern connection framework</change>
+ <change>Use Google Play SSL</change>
+ <change>Show album instead of artist for Show all media</change>
+ <change>Ask for location permissions for Day/Night themes</change>
+ <change>Fix a change to the ChromeCast API</change>
+ <change>Fix Show all media sometimes failing</change>
+ </release>
<release version="5.3.1" versioncode="187" releasedate="10/4/2016">
<change>Fix Internet Radio streams which point to playlists</change>
<change>Don't show playback speed button below Android 6.0</change>