aboutsummaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml11
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java25
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java507
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java168
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Constants.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/EnvironmentVariables.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/FileUtil.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java29
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Util.java11
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java53
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java45
-rw-r--r--app/src/main/res/values-de/strings.xml4
-rw-r--r--app/src/main/res/values-hu/strings.xml12
-rw-r--r--app/src/main/res/values/strings.xml7
-rw-r--r--app/src/main/res/xml/settings_cache.xml8
-rw-r--r--app/src/main/res/xml/settings_playback.xml14
22 files changed, 220 insertions, 714 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 37643a21..06f79977 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -243,18 +243,7 @@
<meta-data android:name="android.app.default_searchable"
android:value="github.daneren2005.dsub.activity.QueryReceiverActivity"/>
-
- <meta-data android:name="com.google.android.backup.api_key"
- android:value="AEdPqrEAAAAIUhOMtwa_eG-f0oYUHnetl_Cz7cO9zae8ZXOK5w"/>
- <meta-data
- android:name="com.google.android.gms.version"
- android:value="@integer/google_play_services_version" />
-
- <meta-data android:name="com.google.android.gms.car.application"
- android:resource="@xml/auto_app_description"/>
- <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
- android:resource="@drawable/stat_notify_playing" />
</application>
</manifest>
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
index 82e50b76..e7c7f1fb 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
@@ -270,6 +270,10 @@ public class MainFragment extends SelectRecyclerFragment<Integer> {
}
private void getLogs() {
+ if (EnvironmentVariables.PASTEBIN_DEV_KEY == null) {
+ Util.toast(context, "No PASTEBIN_DEV_KEY configured - can't upload logs");
+ return;
+ }
try {
final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
new LoadingTask<String>(context) {
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
index 10623b4e..f1b2b9c8 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
@@ -1175,10 +1175,10 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
downloadService.previous();
break;
case ACTION_FORWARD:
- downloadService.seekTo(downloadService.getPlayerPosition() + DownloadService.FAST_FORWARD);
+ downloadService.fastForward();
break;
case ACTION_REWIND:
- downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND);
+ downloadService.rewind();
break;
}
return null;
@@ -1364,7 +1364,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
switch (playerState) {
case DOWNLOADING:
if(currentPlaying != null) {
- if(Util.isWifiRequiredForDownload(context)) {
+ if(Util.isWifiRequiredForDownload(context) || Util.isLocalNetworkRequiredForDownload(context)) {
statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled));
} else {
long bytes = currentPlaying.getPartialFile().length();
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 fa2ca340..94c9b7fc 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
@@ -795,7 +795,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
try {
String url = (String) value;
new URL(url);
- if (url.contains(" ") || url.contains("@") || url.contains("_")) {
+ if (url.contains(" ") || url.contains("@")) {
throw new Exception();
}
} catch (Exception x) {
@@ -816,7 +816,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
}
new URL(url);
- if (url.contains(" ") || url.contains("@") || url.contains("_")) {
+ if (url.contains(" ") || url.contains("@")) {
throw new Exception();
}
} catch (Exception x) {
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 de230309..a8b21be9 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -1601,7 +1601,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
Share share = shares.get(0);
shareExternal(share);
} else {
- Util.toast(context, context.getResources().getString(R.string.playlist_error), false);
+ Util.toast(context, context.getResources().getString(R.string.share_create_error), false);
}
}
@@ -1611,9 +1611,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
} else {
- msg = context.getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
+ msg = context.getResources().getString(R.string.share_create_error) + " " + getErrorMessage(error);
}
+ Log.e(TAG, "Failed to create share", error);
Util.toast(context, msg, false);
}
}.execute();
diff --git a/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java b/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java
index 35f6d37a..88cfe559 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java
@@ -279,12 +279,18 @@ public class AutoMediaBrowserService extends MediaBrowserService {
// music files
for(Entry entry: indexes.getEntries()) {
+ entry.setBookmark(null); // don't resume from a bookmark in a browse listing
+ Bundle extras = new Bundle();
+ extras.putSerializable(Constants.INTENT_EXTRA_ENTRY, entry);
+ extras.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getId());
+
MediaDescription description = new MediaDescription.Builder()
.setTitle(entry.getTitle())
- .setMediaId(MUSIC_DIRECTORY_PREFIX + entry.getId())
+ .setMediaId(entry.getId())
+ .setExtras(extras)
.build();
- mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE));
+ mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_PLAYABLE));
}
result.sendResult(mediaItems);
@@ -315,15 +321,24 @@ public class AutoMediaBrowserService extends MediaBrowserService {
.setTitle(entry.getTitle())
.setMediaId(MUSIC_DIRECTORY_CONTENTS_PREFIX + entry.getId())
.build();
+
+ mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE));
} else {
- // playback options for a single item
+ // mark individual songs as directly playable
+ entry.setBookmark(null); // don't resume from a bookmark in a browse listing
+ Bundle extras = new Bundle();
+ extras.putSerializable(Constants.INTENT_EXTRA_ENTRY, entry);
+ extras.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getId());
+
description = new MediaDescription.Builder()
.setTitle(entry.getTitle())
- .setMediaId(MUSIC_DIRECTORY_PREFIX + entry.getId())
+ .setMediaId(entry.getId())
+ .setExtras(extras)
.build();
+
+ mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_PLAYABLE));
}
- mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE));
}
result.sendResult(mediaItems);
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
deleted file mode 100644
index f9e2bfb1..00000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- This file is part of Subsonic.
- Subsonic is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Subsonic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
- Copyright 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.service;
-
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Bundle;
-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;
-import com.google.android.gms.cast.RemoteMediaPlayer;
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.ResultCallback;
-import com.google.android.gms.common.api.Status;
-import com.google.android.gms.common.images.WebImage;
-
-import java.io.File;
-import java.io.IOException;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.domain.RemoteControlState;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.EnvironmentVariables;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.util.compat.CastCompat;
-import github.daneren2005.serverproxy.FileProxy;
-import github.daneren2005.serverproxy.ServerProxy;
-import github.daneren2005.serverproxy.WebProxy;
-
-/**
- * Created by owner on 2/9/14.
- */
-public class ChromeCastController extends RemoteController {
- private static final String TAG = ChromeCastController.class.getSimpleName();
-
- private CastDevice castDevice;
- private GoogleApiClient apiClient;
-
- private boolean applicationStarted = false;
- private boolean waitingForReconnect = false;
- private boolean error = false;
- private boolean ignoreNextPaused = false;
- private String sessionId;
- private boolean isStopping = false;
- private Runnable afterUpdateComplete = null;
-
- private RemoteMediaPlayer mediaPlayer;
- private double gain = 0.5;
-
- public ChromeCastController(DownloadService downloadService, CastDevice castDevice) {
- super(downloadService);
- this.castDevice = castDevice;
- }
-
- @Override
- public void create(boolean playing, int seconds) {
- downloadService.setPlayerState(PlayerState.PREPARING);
-
- ConnectionCallbacks connectionCallbacks = new ConnectionCallbacks(playing, seconds);
- ConnectionFailedListener connectionFailedListener = new ConnectionFailedListener();
- Cast.Listener castClientListener = new Cast.Listener() {
- @Override
- public void onApplicationStatusChanged() {
- if (apiClient != null && apiClient.isConnected()) {
- Log.i(TAG, "onApplicationStatusChanged: " + Cast.CastApi.getApplicationStatus(apiClient));
- }
- }
-
- @Override
- public void onVolumeChanged() {
- if (apiClient != null && applicationStarted) {
- try {
- gain = Cast.CastApi.getVolume(apiClient);
- } catch (Exception e) {
- Log.w(TAG, "Failed to get volume");
- }
- }
- }
-
- @Override
- public void onApplicationDisconnected(int errorCode) {
- shutdownInternal();
- }
-
- };
-
- Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(castDevice, castClientListener).setVerboseLoggingEnabled(true);
- apiClient = new GoogleApiClient.Builder(downloadService).useDefaultAccount()
- .addApi(Cast.API, apiOptionsBuilder.build())
- .addConnectionCallbacks(connectionCallbacks)
- .addOnConnectionFailedListener(connectionFailedListener)
- .build();
-
- apiClient.connect();
- }
-
- @Override
- public void start() {
- if(error) {
- error = false;
- Log.w(TAG, "Attempting to restart song");
- startSong(downloadService.getCurrentPlaying(), true, 0);
- return;
- }
-
- try {
- mediaPlayer.play(apiClient);
- } catch(Exception e) {
- Log.e(TAG, "Failed to start");
- }
- }
-
- @Override
- public void stop() {
- try {
- mediaPlayer.pause(apiClient);
- } catch(Exception e) {
- Log.e(TAG, "Failed to pause");
- }
- }
-
- @Override
- public void shutdown() {
- try {
- if(mediaPlayer != null && !error) {
- mediaPlayer.stop(apiClient);
- }
- } catch(Exception e) {
- Log.e(TAG, "Failed to stop mediaPlayer", e);
- }
-
- try {
- if(apiClient != null) {
- Cast.CastApi.stopApplication(apiClient);
- Cast.CastApi.removeMessageReceivedCallbacks(apiClient, mediaPlayer.getNamespace());
- mediaPlayer = null;
- applicationStarted = false;
- }
- } catch(Exception e) {
- Log.e(TAG, "Failed to shutdown application", e);
- }
-
- if(apiClient != null && apiClient.isConnected()) {
- apiClient.disconnect();
- }
- apiClient = null;
-
- if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
- }
-
- private void shutdownInternal() {
- // This will call this.shutdown() indirectly
- downloadService.setRemoteEnabled(RemoteControlState.LOCAL, null);
- }
-
- @Override
- public void updatePlaylist() {
- if(downloadService.getCurrentPlaying() == null) {
- startSong(null, false, 0);
- }
- }
-
- @Override
- public void changePosition(int seconds) {
- try {
- mediaPlayer.seek(apiClient, seconds * 1000L);
- } catch(Exception e) {
- Log.e(TAG, "FAiled to seek to " + seconds);
- }
- }
-
- @Override
- public void changeTrack(int index, DownloadFile song) {
- startSong(song, true, 0);
- }
-
- @Override
- public void setVolume(int volume) {
- gain = volume / 10.0;
-
- try {
- Cast.CastApi.setVolume(apiClient, gain);
- } catch(Exception e) {
- Log.e(TAG, "Failed to the volume");
- }
- }
- @Override
- public void updateVolume(boolean up) {
- double delta = up ? 0.1 : -0.1;
- gain += delta;
- gain = Math.max(gain, 0.0);
- gain = Math.min(gain, 1.0);
-
- try {
- Cast.CastApi.setVolume(apiClient, gain);
- } catch(Exception e) {
- Log.e(TAG, "Failed to the volume");
- }
- }
- @Override
- public double getVolume() {
- return Cast.CastApi.getVolume(apiClient);
- }
-
- @Override
- public int getRemotePosition() {
- if(mediaPlayer != null) {
- return (int) (mediaPlayer.getApproximateStreamPosition() / 1000L);
- } else {
- return 0;
- }
- }
-
- @Override
- public int getRemoteDuration() {
- if(mediaPlayer != null) {
- return (int) (mediaPlayer.getStreamDuration() / 1000L);
- } else {
- return 0;
- }
- }
-
- void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) {
- if(currentPlaying == null) {
- try {
- if (mediaPlayer != null && !error && !isStopping) {
- isStopping = true;
- mediaPlayer.stop(apiClient).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
- @Override
- public void onResult(RemoteMediaPlayer.MediaChannelResult mediaChannelResult) {
- isStopping = false;
-
- if(afterUpdateComplete != null) {
- afterUpdateComplete.run();
- afterUpdateComplete = null;
- }
- }
- });
- }
- } catch(Exception e) {
- // Just means it didn't need to be stopped
- }
- downloadService.setPlayerState(PlayerState.IDLE);
- return;
- } else if(isStopping) {
- afterUpdateComplete = new Runnable() {
- @Override
- public void run() {
- startSong(currentPlaying, autoStart, position);
- }
- };
- return;
- }
-
- downloadService.setPlayerState(PlayerState.PREPARING);
- MusicDirectory.Entry song = currentPlaying.getSong();
-
- try {
- MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url = getStreamUrl(musicService, currentPlaying);
-
- // Setup song/video information
- MediaMetadata meta = new MediaMetadata(song.isVideo() ? MediaMetadata.MEDIA_TYPE_MOVIE : MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
- meta.putString(MediaMetadata.KEY_TITLE, song.getTitle());
- if(song.getTrack() != null) {
- meta.putInt(MediaMetadata.KEY_TRACK_NUMBER, song.getTrack());
- }
- if(!song.isVideo()) {
- meta.putString(MediaMetadata.KEY_ARTIST, song.getArtist());
- meta.putString(MediaMetadata.KEY_ALBUM_ARTIST, song.getArtist());
- meta.putString(MediaMetadata.KEY_ALBUM_TITLE, song.getAlbum());
-
- if(castDevice.hasCapability(CastDevice.CAPABILITY_VIDEO_OUT)) {
- if (proxy == null || proxy instanceof WebProxy) {
- String coverArt = musicService.getCoverArtUrl(downloadService, song);
-
- // If proxy is going, it is a web proxy
- if (proxy != null) {
- coverArt = proxy.getPublicAddress(coverArt);
- }
-
- meta.addImage(new WebImage(Uri.parse(coverArt)));
- } else {
- File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song);
- if (coverArtFile != null && coverArtFile.exists()) {
- String coverArt = proxy.getPublicAddress(coverArtFile.getPath());
- meta.addImage(new WebImage(Uri.parse(coverArt)));
- }
- }
- }
- }
-
- String contentType;
- if(song.isVideo()) {
- contentType = "application/x-mpegURL";
- }
- else if(song.getTranscodedContentType() != null) {
- contentType = song.getTranscodedContentType();
- } else if(song.getContentType() != null) {
- contentType = song.getContentType();
- } else {
- contentType = "audio/mpeg";
- }
-
- // Load it into a MediaInfo wrapper
- MediaInfo mediaInfo = new MediaInfo.Builder(url)
- .setContentType(contentType)
- .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
- .setMetadata(meta)
- .build();
-
- if(autoStart) {
- ignoreNextPaused = true;
- }
-
- ResultCallback callback = new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
- @Override
- 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();
- }
- }
- };
-
- if(position > 0) {
- mediaPlayer.load(apiClient, mediaInfo, autoStart, position * 1000L).setResultCallback(callback);
- } else {
- mediaPlayer.load(apiClient, mediaInfo, autoStart).setResultCallback(callback);
- }
- } catch (IllegalStateException e) {
- Log.e(TAG, "Problem occurred with media during loading", e);
- failedLoad();
- } catch (Exception e) {
- Log.e(TAG, "Problem opening media during loading", e);
- failedLoad();
- }
- }
-
- private void failedLoad() {
- Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
- downloadService.setPlayerState(PlayerState.STOPPED);
- error = true;
- }
-
-
- private class ConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks {
- private boolean isPlaying;
- private int position;
- private ResultCallback<Cast.ApplicationConnectionResult> resultCallback;
-
- ConnectionCallbacks(boolean isPlaying, int position) {
- this.isPlaying = isPlaying;
- this.position = position;
-
- resultCallback = new ResultCallback<Cast.ApplicationConnectionResult>() {
- @Override
- public void onResult(Cast.ApplicationConnectionResult result) {
- Status status = result.getStatus();
- if (status.isSuccess()) {
- ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
- sessionId = result.getSessionId();
- String applicationStatus = result.getApplicationStatus();
- boolean wasLaunched = result.getWasLaunched();
-
- applicationStarted = true;
- setupChannel();
- } else {
- shutdownInternal();
- }
- }
- };
- }
-
- @Override
- public void onConnected(Bundle connectionHint) {
- if (waitingForReconnect) {
- Log.i(TAG, "Reconnecting");
- reconnectApplication();
- } else {
- launchApplication();
- }
- }
-
- @Override
- public void onConnectionSuspended(int cause) {
- Log.w(TAG, "Connection suspended");
- isPlaying = downloadService.getPlayerState() == PlayerState.STARTED;
- position = getRemotePosition();
- waitingForReconnect = true;
- }
-
- void launchApplication() {
- try {
- Cast.CastApi.launchApplication(apiClient, EnvironmentVariables.CAST_APPLICATION_ID, false).setResultCallback(resultCallback);
- } catch (Exception e) {
- Log.e(TAG, "Failed to launch application", e);
- }
- }
- void reconnectApplication() {
- try {
- Cast.CastApi.joinApplication(apiClient, EnvironmentVariables.CAST_APPLICATION_ID, sessionId).setResultCallback(resultCallback);
- } catch (Exception e) {
- Log.e(TAG, "Failed to reconnect application", e);
- }
- }
- void setupChannel() {
- if(!waitingForReconnect) {
- mediaPlayer = new RemoteMediaPlayer();
- mediaPlayer.setOnStatusUpdatedListener(new RemoteMediaPlayer.OnStatusUpdatedListener() {
- @Override
- public void onStatusUpdated() {
- MediaStatus mediaStatus = mediaPlayer.getMediaStatus();
- if (mediaStatus == null) {
- return;
- }
-
- switch (mediaStatus.getPlayerState()) {
- case MediaStatus.PLAYER_STATE_PLAYING:
- if (ignoreNextPaused) {
- ignoreNextPaused = false;
- }
- downloadService.setPlayerState(PlayerState.STARTED);
- break;
- case MediaStatus.PLAYER_STATE_PAUSED:
- if (!ignoreNextPaused) {
- downloadService.setPlayerState(PlayerState.PAUSED);
- }
- break;
- case MediaStatus.PLAYER_STATE_BUFFERING:
- downloadService.setPlayerState(PlayerState.PREPARING);
- break;
- case MediaStatus.PLAYER_STATE_IDLE:
- if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
- if(downloadService.getPlayerState() != PlayerState.PREPARING) {
- downloadService.onSongCompleted();
- }
- } else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_INTERRUPTED) {
- if (downloadService.getPlayerState() != PlayerState.PREPARING) {
- downloadService.setPlayerState(PlayerState.PREPARING);
- }
- } else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_ERROR) {
- Log.e(TAG, "Idle due to unknown error");
- downloadService.onSongCompleted();
- } else {
- Log.w(TAG, "Idle reason: " + mediaStatus.getIdleReason());
- downloadService.setPlayerState(PlayerState.IDLE);
- }
- break;
- }
- }
- });
- }
-
- try {
- Cast.CastApi.setMessageReceivedCallbacks(apiClient, mediaPlayer.getNamespace(), mediaPlayer);
- } catch (Exception e) {
- Log.e(TAG, "Exception while creating channel", e);
- }
-
- if(!waitingForReconnect) {
- DownloadFile currentPlaying = downloadService.getCurrentPlaying();
- startSong(currentPlaying, isPlaying, position);
- }
- if(waitingForReconnect) {
- waitingForReconnect = false;
- }
- }
- }
-
- private class ConnectionFailedListener implements GoogleApiClient.OnConnectionFailedListener {
- @Override
- public void onConnectionFailed(ConnectionResult result) {
- shutdownInternal();
- }
- }
-}
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 8213a7d4..a4ca705c 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
@@ -68,6 +68,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.concurrent.CopyOnWriteArrayList;
import android.annotation.TargetApi;
import android.app.Service;
@@ -107,12 +108,11 @@ public class DownloadService extends Service {
public static final String CMD_NEXT = "github.daneren2005.dsub.CMD_NEXT";
public static final String CANCEL_DOWNLOADS = "github.daneren2005.dsub.CANCEL_DOWNLOADS";
public static final String START_PLAY = "github.daneren2005.dsub.START_PLAYING";
- public static final int FAST_FORWARD = 30000;
- public static final int REWIND = 10000;
private static final long DEFAULT_DELAY_UPDATE_PROGRESS = 1000L;
private static final double DELETE_CUTOFF = 0.84;
private static final int REQUIRED_ALBUM_MATCHES = 4;
- private static final int REMOTE_PLAYLIST_TOTAL = 3;
+ private static final int REMOTE_PLAYLIST_PREV = 10;
+ private static final int REMOTE_PLAYLIST_NEXT = 40;
private static final int SHUFFLE_MODE_NONE = 0;
private static final int SHUFFLE_MODE_ALL = 1;
private static final int SHUFFLE_MODE_ARTIST = 2;
@@ -155,7 +155,7 @@ public class DownloadService extends Service {
private boolean removePlayed;
private boolean shufflePlay;
private boolean artistRadio;
- private final List<OnSongChangedListener> onSongChangedListeners = new ArrayList<>();
+ private final CopyOnWriteArrayList<OnSongChangedListener> onSongChangedListeners = new CopyOnWriteArrayList<>();
private long revision;
private static DownloadService instance;
private String suggestedPlaylistName;
@@ -509,14 +509,17 @@ public class DownloadService extends Service {
private synchronized void updateRemotePlaylist() {
List<DownloadFile> playlist = new ArrayList<>();
if(currentPlaying != null) {
- int index = downloadList.indexOf(currentPlaying);
- if(index == -1) {
- index = 0;
+ int startIndex = downloadList.indexOf(currentPlaying) - REMOTE_PLAYLIST_PREV;
+ if(startIndex < 0) {
+ startIndex = 0;
}
int size = size();
- int end = index + REMOTE_PLAYLIST_TOTAL;
- for(int i = index; i < size && i < end; i++) {
+ int endIndex = downloadList.indexOf(currentPlaying) + REMOTE_PLAYLIST_NEXT;
+ if(endIndex > size) {
+ endIndex = size;
+ }
+ for(int i = startIndex; i < endIndex; i++) {
playlist.add(downloadList.get(i));
}
}
@@ -1038,6 +1041,15 @@ public class DownloadService extends Service {
return temp;
}
+ public synchronized List<DownloadFile> getRecentDownloads() {
+ int from = Math.max(currentPlayingIndex - 10, 0);
+ int songsToKeep = Math.max(Util.getPreloadCount(this), 20);
+ int to = Math.min(currentPlayingIndex + songsToKeep, downloadList.size() - 1);
+ List<DownloadFile> temp = downloadList.subList(from, to);
+ temp.addAll(backgroundDownloadList);
+ return temp;
+ }
+
public List<DownloadFile> getBackgroundDownloads() {
return backgroundDownloadList;
}
@@ -1178,10 +1190,10 @@ public class DownloadService extends Service {
}
}
public synchronized int rewind() {
- return seekToWrapper(-REWIND);
+ return seekToWrapper(Integer.parseInt(Util.getPreferences(this).getString(Constants.PREFERENCES_KEY_REWIND_INTERVAL, "10"))*-1000);
}
public synchronized int fastForward() {
- return seekToWrapper(FAST_FORWARD);
+ return seekToWrapper(Integer.parseInt(Util.getPreferences(this).getString(Constants.PREFERENCES_KEY_FASTFORWARD_INTERVAL, "30"))*1000);
}
protected int seekToWrapper(int difference) {
int msPlayed = Math.max(0, getPlayerPosition());
@@ -2605,7 +2617,7 @@ public class DownloadService extends Service {
// Check forwards
for(int i = index + 1; i < downloadList.size() && matched < REQUIRED_ALBUM_MATCHES; i++) {
- if(albumName.equals(downloadList.get(i).getSong().getAlbum())) {
+ if(Util.equals(albumName, downloadList.get(i).getSong().getAlbum())) {
matched++;
} else {
break;
@@ -2614,7 +2626,7 @@ public class DownloadService extends Service {
// Check backwards
for(int i = index - 1; i >= 0 && matched < REQUIRED_ALBUM_MATCHES; i--) {
- if(albumName.equals(downloadList.get(i).getSong().getAlbum())) {
+ if(Util.equals(albumName, downloadList.get(i).getSong().getAlbum())) {
matched++;
} else {
break;
@@ -2789,12 +2801,7 @@ public class DownloadService extends Service {
addOnSongChangedListener(listener, false);
}
public void addOnSongChangedListener(OnSongChangedListener listener, boolean run) {
- synchronized(onSongChangedListeners) {
- int index = onSongChangedListeners.indexOf(listener);
- if (index == -1) {
- onSongChangedListeners.add(listener);
- }
- }
+ onSongChangedListeners.addIfAbsent(listener);
if(run) {
if(mediaPlayerHandler != null) {
@@ -2813,56 +2820,47 @@ public class DownloadService extends Service {
}
}
public void removeOnSongChangeListener(OnSongChangedListener listener) {
- synchronized(onSongChangedListeners) {
- int index = onSongChangedListeners.indexOf(listener);
- if (index != -1) {
- onSongChangedListeners.remove(index);
- }
- }
+ onSongChangedListeners.remove(listener);
}
private void onSongChanged() {
final long atRevision = revision;
- synchronized(onSongChangedListeners) {
- final boolean shouldFastForward = shouldFastForward();
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onSongChanged(currentPlaying, currentPlayingIndex, shouldFastForward);
+ final boolean shouldFastForward = shouldFastForward();
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onSongChanged(currentPlaying, currentPlayingIndex, shouldFastForward);
- MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
- listener.onMetadataUpdate(entry, METADATA_UPDATED_ALL);
- }
+ MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
+ listener.onMetadataUpdate(entry, METADATA_UPDATED_ALL);
}
- });
- }
+ }
+ });
+ }
- if (mediaPlayerHandler != null && !onSongChangedListeners.isEmpty()) {
- mediaPlayerHandler.post(new Runnable() {
- @Override
- public void run() {
- onSongProgress();
- }
- });
- }
+ if (mediaPlayerHandler != null && !onSongChangedListeners.isEmpty()) {
+ mediaPlayerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onSongProgress();
+ }
+ });
}
}
private void onSongsChanged() {
final long atRevision = revision;
- synchronized(onSongChangedListeners) {
- final boolean shouldFastForward = shouldFastForward();
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex, shouldFastForward);
- }
+ final boolean shouldFastForward = shouldFastForward();
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex, shouldFastForward);
}
- });
- }
+ }
+ });
}
}
@@ -2877,17 +2875,15 @@ public class DownloadService extends Service {
final int index = getCurrentPlayingIndex();
final int queueSize = size();
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onSongProgress(currentPlaying, position, duration, isSeekable);
- }
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onSongProgress(currentPlaying, position, duration, isSeekable);
}
- });
- }
+ }
+ });
}
if(manual) {
@@ -2910,35 +2906,31 @@ public class DownloadService extends Service {
}
private void onStateUpdate() {
final long atRevision = revision;
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onStateUpdate(currentPlaying, playerState);
- }
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onStateUpdate(currentPlaying, playerState);
}
- });
- }
+ }
+ });
}
}
public void onMetadataUpdate() {
onMetadataUpdate(METADATA_UPDATED_ALL);
}
public void onMetadataUpdate(final int updateType) {
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (instance != null) {
- MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
- listener.onMetadataUpdate(entry, updateType);
- }
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (instance != null) {
+ MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
+ listener.onMetadataUpdate(entry, updateType);
}
- });
- }
+ }
+ });
}
handler.post(new Runnable() {
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 657ac4a9..a4987b09 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -42,8 +42,6 @@ import android.net.NetworkInfo;
import android.util.Base64;
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;
@@ -81,6 +79,8 @@ import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SongDBHandler;
import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.util.compat.GoogleCompat;
+
import java.io.*;
import java.util.Map;
import java.util.zip.GZIPInputStream;
@@ -1875,7 +1875,7 @@ public class RESTMusicService implements MusicService {
private HttpURLConnection getConnectionDirect(Context context, String url, Map<String, String> headers, int minNetworkTimeout) throws Exception {
if(!hasInstalledGoogleSSL) {
try {
- ProviderInstaller.installIfNeeded(context);
+ GoogleCompat.installProvider(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);
diff --git a/app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java b/app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java
index ac8fa72a..f160e176 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/CacheCleaner.java
@@ -151,7 +151,7 @@ public class CacheCleaner {
private Set<File> findUndeletableFiles() {
Set<File> undeletable = new HashSet<File>(5);
- for (DownloadFile downloadFile : downloadService.getDownloads()) {
+ for (DownloadFile downloadFile : downloadService.getRecentDownloads()) {
undeletable.add(downloadFile.getPartialFile());
undeletable.add(downloadFile.getCompleteFile());
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
index 21adce8c..e0a1e164 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
@@ -103,6 +103,7 @@ public final class Constants {
public static final String PREFERENCES_KEY_SCROBBLE = "scrobble";
public static final String PREFERENCES_KEY_REPEAT_MODE = "repeatMode";
public static final String PREFERENCES_KEY_WIFI_REQUIRED_FOR_DOWNLOAD = "wifiRequiredForDownload";
+ public static final String PREFERENCES_KEY_LOCAL_NETWORK_REQUIRED_FOR_DOWNLOAD = "localNetworkRequiredForDownload";
public static final String PREFERENCES_KEY_RANDOM_SIZE = "randomSize";
public static final String PREFERENCES_KEY_SLEEP_TIMER_DURATION = "sleepTimerDuration";
public static final String PREFERENCES_KEY_OFFLINE = "offline";
@@ -179,7 +180,9 @@ public final class Constants {
public static final String PREFERENCES_KEY_PLAYBACK_SPEED = "playbackSpeed";
public static final String PREFERENCES_KEY_SONG_PLAYBACK_SPEED = "songPlaybackSpeed";
public static final String PREFERENCES_KEY_DLNA_CASTING_ENABLED = "dlnaCastingEnabled";
-
+ public static final String PREFERENCES_KEY_REWIND_INTERVAL = "rewindInterval";
+ public static final String PREFERENCES_KEY_FASTFORWARD_INTERVAL = "fastforwardInterval";
+
public static final String OFFLINE_SCROBBLE_COUNT = "scrobbleCount";
public static final String OFFLINE_SCROBBLE_ID = "scrobbleID";
public static final String OFFLINE_SCROBBLE_SEARCH = "scrobbleTitle";
diff --git a/app/src/main/java/github/daneren2005/dsub/util/EnvironmentVariables.java b/app/src/main/java/github/daneren2005/dsub/util/EnvironmentVariables.java
index 710d5232..8af74f07 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/EnvironmentVariables.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/EnvironmentVariables.java
@@ -16,6 +16,6 @@
package github.daneren2005.dsub.util;
public final class EnvironmentVariables {
- public static final String PASTEBIN_DEV_KEY = "";
- public static final String CAST_APPLICATION_ID = "";
+ public static final String PASTEBIN_DEV_KEY = null;
+ public static final String CAST_APPLICATION_ID = null;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
index 04dc73b7..fb395c77 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
@@ -67,8 +67,8 @@ public class FileUtil {
private static final String TAG = FileUtil.class.getSimpleName();
private static final String[] FILE_SYSTEM_UNSAFE = {"/", "\\", "..", ":", "\"", "?", "*", "<", ">", "|"};
private static final String[] FILE_SYSTEM_UNSAFE_DIR = {"\\", "..", ":", "\"", "?", "*", "<", ">", "|"};
- private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "aac", "flac", "m4a", "wav", "wma");
- private static final List<String> VIDEO_FILE_EXTENSIONS = Arrays.asList("flv", "mp4", "m4v", "wmv", "avi", "mov", "mpg", "mkv");
+ private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "aac", "flac", "m4a", "wav", "wma", "opus", "oga");
+ private static final List<String> VIDEO_FILE_EXTENSIONS = Arrays.asList("flv", "mp4", "m4v", "wmv", "avi", "mov", "mpg", "mkv", "3gp", "webm");
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Arrays.asList("m3u");
private static final int MAX_FILENAME_LENGTH = 254 - ".complete.mp3".length();
private static File DEFAULT_MUSIC_DIR;
diff --git a/app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java b/app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java
index 73ec6aec..e19cc156 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/MediaRouteManager.java
@@ -19,10 +19,6 @@ import android.os.Build;
import android.support.v7.media.MediaRouteProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GooglePlayServicesUtil;
import java.util.ArrayList;
import java.util.List;
@@ -32,7 +28,7 @@ import github.daneren2005.dsub.provider.DLNARouteProvider;
import github.daneren2005.dsub.provider.JukeboxRouteProvider;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.RemoteController;
-import github.daneren2005.dsub.util.compat.CastCompat;
+import github.daneren2005.dsub.util.compat.GoogleCompat;
import static android.support.v7.media.MediaRouter.RouteInfo;
@@ -50,25 +46,10 @@ public class MediaRouteManager extends MediaRouter.Callback {
private List<MediaRouteProvider> onlineProviders = new ArrayList<MediaRouteProvider>();
private DLNARouteProvider dlnaProvider;
- static {
- try {
- CastCompat.checkAvailable();
- castAvailable = true;
- } catch(Throwable t) {
- castAvailable = false;
- }
- }
-
public MediaRouteManager(DownloadService downloadService) {
this.downloadService = downloadService;
router = MediaRouter.getInstance(downloadService);
-
- // Check if play services is available
- int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(downloadService);
- if(result != ConnectionResult.SUCCESS){
- Log.w(TAG, "No play services, failed with result: " + result);
- castAvailable = false;
- }
+ castAvailable = GoogleCompat.playServicesAvailable(downloadService) && GoogleCompat.castAvailable();
addProviders();
buildSelector();
@@ -83,7 +64,7 @@ public class MediaRouteManager extends MediaRouter.Callback {
@Override
public void onRouteSelected(MediaRouter router, RouteInfo info) {
if(castAvailable) {
- RemoteController controller = CastCompat.getController(downloadService, info);
+ RemoteController controller = GoogleCompat.getController(downloadService, info);
if(controller != null) {
downloadService.setRemoteEnabled(RemoteControlState.CHROMECAST, controller);
}
@@ -137,7 +118,7 @@ public class MediaRouteManager extends MediaRouter.Callback {
}
public RemoteController getRemoteController(RouteInfo info) {
if(castAvailable) {
- return CastCompat.getController(downloadService, info);
+ return GoogleCompat.getController(downloadService, info);
} else {
return null;
}
@@ -170,7 +151,7 @@ public class MediaRouteManager extends MediaRouter.Callback {
builder.addControlCategory(JukeboxRouteProvider.CATEGORY_JUKEBOX_ROUTE);
}
if(castAvailable) {
- builder.addControlCategory(CastCompat.getCastControlCategory());
+ builder.addControlCategory(GoogleCompat.getCastControlCategory());
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
builder.addControlCategory(DLNARouteProvider.CATEGORY_DLNA);
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 7f8a168d..93004a48 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
@@ -36,6 +36,7 @@ import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.net.ConnectivityManager;
+import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
@@ -1082,7 +1083,10 @@ public final class Util {
boolean wifiConnected = connected && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
boolean wifiRequired = isWifiRequiredForDownload(context);
- return connected && (!wifiRequired || wifiConnected);
+ boolean isLocalNetwork = connected && !networkInfo.isRoaming();
+ boolean localNetworkRequired = isLocalNetworkRequiredForDownload(context);
+
+ return connected && (!wifiRequired || wifiConnected) && (!localNetworkRequired || isLocalNetwork);
} else {
return connected;
}
@@ -1116,6 +1120,11 @@ public final class Util {
return prefs.getBoolean(Constants.PREFERENCES_KEY_WIFI_REQUIRED_FOR_DOWNLOAD, false);
}
+ public static boolean isLocalNetworkRequiredForDownload(Context context) {
+ SharedPreferences prefs = getPreferences(context);
+ return prefs.getBoolean(Constants.PREFERENCES_KEY_LOCAL_NETWORK_REQUIRED_FOR_DOWNLOAD, false);
+ }
+
public static void info(Context context, int titleId, int messageId) {
info(context, titleId, messageId, true);
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java b/app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java
deleted file mode 100644
index 415106db..00000000
--- a/app/src/main/java/github/daneren2005/dsub/util/compat/CastCompat.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- This file is part of Subsonic.
- Subsonic is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Subsonic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
- Copyright 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.util.compat;
-
-import android.support.v7.media.MediaRouter;
-
-import com.google.android.gms.cast.CastDevice;
-import com.google.android.gms.cast.CastMediaControlIntent;
-
-import github.daneren2005.dsub.service.ChromeCastController;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.RemoteController;
-import github.daneren2005.dsub.util.EnvironmentVariables;
-
-public final class CastCompat {
- static {
- try {
- Class.forName("com.google.android.gms.cast.CastDevice");
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- public static void checkAvailable() throws Throwable {
- // Calling here forces class initialization.
- }
-
- public static RemoteController getController(DownloadService downloadService, MediaRouter.RouteInfo info) {
- CastDevice device = CastDevice.getFromBundle(info.getExtras());
- if(device != null) {
- return new ChromeCastController(downloadService, device);
- } else {
- return null;
- }
- }
-
- public static String getCastControlCategory() {
- return CastMediaControlIntent.categoryForCast(EnvironmentVariables.CAST_APPLICATION_ID);
- }
-}
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 00bca833..816a071d 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
@@ -370,6 +370,30 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
private void playMusicDirectory(Entry dir, boolean shuffle, boolean append, boolean playFromBookmark) {
playMusicDirectory(dir.getId(), shuffle, append, playFromBookmark);
}
+ private void playMusicDirectory(final String dirId, final boolean shuffle, final boolean append, final Entry startEntry) {
+ new SilentServiceTask<Void>(downloadService) {
+ @Override
+ protected Void doInBackground(MusicService musicService) throws Throwable {
+ MusicDirectory musicDirectory;
+ if(Util.isTagBrowsing(downloadService) && !Util.isOffline(downloadService)) {
+ musicDirectory = musicService.getAlbum(dirId, "dir", false, downloadService, null);
+ } else {
+ musicDirectory = musicService.getMusicDirectory(dirId, "dir", false, downloadService, null);
+ }
+
+ List<Entry> playEntries = new ArrayList<>();
+ List<Entry> allEntries = musicDirectory.getChildren(false, true);
+ for(Entry song: allEntries) {
+ if (!song.isVideo() && song.getRating() != 1) {
+ playEntries.add(song);
+ }
+ }
+ playSongs(playEntries, shuffle, append, startEntry);
+
+ return null;
+ }
+ }.execute();
+ }
private void playMusicDirectory(final String dirId, final boolean shuffle, final boolean append, final boolean playFromBookmark) {
new SilentServiceTask<Void>(downloadService) {
@Override
@@ -409,6 +433,19 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
private void playSongs(List<Entry> entries, boolean shuffle, boolean append) {
playSongs(entries, shuffle, append, false);
}
+ private void playSongs(List<Entry> entries, boolean shuffle, boolean append, Entry startEntry) {
+ if(!append) {
+ downloadService.clear();
+ }
+
+ int startIndex = entries.indexOf(startEntry);
+ int startPosition = 0;
+ if(startEntry.getBookmark() != null) {
+ Bookmark bookmark = startEntry.getBookmark();
+ startPosition = bookmark.getPosition();
+ }
+ downloadService.download(entries, false, true, !append, shuffle, startIndex, startPosition);
+ }
private void playSongs(List<Entry> entries, boolean shuffle, boolean append, boolean resumeFromBookmark) {
if(!append) {
downloadService.clear();
@@ -429,7 +466,7 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
}
}
- downloadService.download(entries, false, !append, false, shuffle, startIndex, startPosition);
+ downloadService.download(entries, false, true, !append, shuffle, startIndex, startPosition);
}
private void noResults() {
@@ -564,13 +601,13 @@ public class RemoteControlClientLP extends RemoteControlClientBase {
playSong(entry, true);
}
- // Currently only happens when playing bookmarks so we should be looking up parent
+ // Enqueue an entire directory when selecting a bookmark or a song
String childId = extras.getString(Constants.INTENT_EXTRA_NAME_CHILD_ID, null);
if(childId != null) {
if(Util.isTagBrowsing(downloadService) && !Util.isOffline(downloadService)) {
- playMusicDirectory(entry.getAlbumId(), shuffle, playLast, true);
+ playMusicDirectory(entry.getAlbumId(), shuffle, playLast, entry);
} else {
- playMusicDirectory(entry.getParent(), shuffle, playLast, true);
+ playMusicDirectory(entry.getParent(), shuffle, playLast, entry);
}
}
}
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index c43f3aea..5772e580 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -168,7 +168,7 @@
<string name="download.empty">Wiedergabeliste ist leer</string>
<string name="download.shuffle_loading">Wiedergabeliste wird gemischt...</string>
<string name="download.playerstate_downloading">Downloade - %s</string>
- <string name="download.playerstate_mobile_disabled">Warte auf Wi-Fi für den Download</string>
+ <string name="download.playerstate_mobile_disabled">Warte auf Wi-Fi oder lokales Netzwerk für den Download</string>
<string name="download.playerstate_buffering">Buffere</string>
<string name="download.playerstate_playing_shuffle">Playing shuffle</string>
<string name="download.menu_show_album">Zeige Album</string>
@@ -301,6 +301,8 @@
<string name="settings.max_bitrate_unlimited">Unbegrenzt</string>
<string name="settings.wifi_required_title">Nur Wi-Fi streaming</string>
<string name="settings.wifi_required_summary">Medien nur streamen, wenn mit Wi-Fi verbunden</string>
+ <string name="settings.local_network_required_title">Kein streaming mit Roaming-Netzwerken</string>
+ <string name="settings.local_network_required_summary">Medien nur streamen, wenn mit lokalem Anbieter verbunden</string>
<string name="settings.network_timeout_title">Netzwerk Timeout</string>
<string name="settings.network_timeout_10000">10 Sekunden</string>
<string name="settings.network_timeout_15000">15 Sekunden</string>
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 12102043..3416a471 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -307,8 +307,8 @@
<string name="settings.theme_dark">Sötét</string>
<string name="settings.theme_black">Fekete</string>
<string name="settings.theme_holo">Holo</string>
- <string name="settings.theme_day_night">Nappal/Éjszaka</string>
- <string name="settings.theme_day_black_night">Nappal/Fekete éjszaka</string>
+ <string name="settings.theme_day_night">Nappali/Éjszakai</string>
+ <string name="settings.theme_day_black_night">Nappali/Éjszakai (fekete)</string>
<string name="settings.theme_fullscreen">Teljes képernyős</string>
<string name="settings.theme_fullscreen_summary">Teljes képernyős üzemmód (értesítési sáv elrejtése).</string>
<string name="settings.track_title">Dalsorszám megjelenítése</string>
@@ -321,6 +321,7 @@
<string name="settings.max_bitrate_wifi">Max. audió bitráta - Wi-Fi</string>
<string name="settings.max_bitrate_mobile">Max. audió bitráta - Mobilhálózat</string>
<string name="settings.max_bitrate_32">32 Kbps</string>
+ <string name="settings.max_bitrate_48">48 Kbps</string>
<string name="settings.max_bitrate_64">64 Kbps</string>
<string name="settings.max_bitrate_80">80 Kbps</string>
<string name="settings.max_bitrate_96">96 Kbps</string>
@@ -488,6 +489,8 @@
<string name="settings.heads_up_notification_summary">Lejátszási értesítések megjelenítése felugró értesítésekként (Android Lollipop+).</string>
<string name="settings.casting_cache">Casting közbeni gyorsítótárazás</string>
<string name="settings.casting_cache_summary">Az éppen lejátszott dal gyorsítótárazása a tartalomátküldés (Casting) alatt.</string>
+ <string name="settings.casting.dlna_casting_enabled">DLNA engedélyezve</string>
+ <string name="settings.casting.dlna_casting_enabled.summary">Ha akkumulátor-merülési probléma lépne fel Android 7.0 alatt, kapcsolja ki a funkciót!</string>
<string name="shuffle.title">Dalsorrend keverése</string>
<string name="shuffle.startYear">Kezdő év:</string>
@@ -613,6 +616,7 @@
<string name="details.title.podcast">Podcast információi</string>
<string name="details.title.playlist">Lejátszási lista információi</string>
<string name="details.title.artist">Előadó információi</string>
+ <string name="details.title.internet_radio_station">Internet Radio részletezés</string>
<string name="details.podcast">Podcast</string>
<string name="details.status">Státusz</string>
<string name="details.artist">Előadó</string>
@@ -652,6 +656,10 @@
<string name="details.last_played">Utoljára lejátszott</string>
<string name="details.expiration">Lejárati idő</string>
<string name="details.played_count">Lejátszások száma</string>
+ <string name="details.stream_url">Stream URL</string>
+ <string name="details.home_page">Honlap</string>
+
+ <string name="permission.external_storage.failed">A DSub nem tud működni tárhely-írási engedély nélkül!</string>
<plurals name="select_album_n_songs">
<item quantity="zero">Nincsenek dalok</item>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b54e4ff2..be1a7bff 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -180,7 +180,7 @@
<string name="download.empty">Playlist is empty</string>
<string name="download.shuffle_loading">Shuffle list is loading...</string>
<string name="download.playerstate_downloading">Downloading - %s</string>
- <string name="download.playerstate_mobile_disabled">Waiting for WiFi network to download</string>
+ <string name="download.playerstate_mobile_disabled">Waiting for WiFi or local (non-roaming) network to download</string>
<string name="download.playerstate_buffering">Buffering</string>
<string name="download.playerstate_playing_shuffle">Shuffle mode</string>
<string name="download.playerstate_playing_artist_radio">Artist radio</string>
@@ -346,6 +346,8 @@
<string name="settings.max_bitrate_unlimited">Unlimited</string>
<string name="settings.wifi_required_title">Wi-Fi streaming only</string>
<string name="settings.wifi_required_summary">Only stream media if connected to Wi-Fi</string>
+ <string name="settings.local_network_required_title">Don\'t stream when roaming</string>
+ <string name="settings.local_network_required_summary">Don\'t stream media while roaming</string>
<string name="settings.network_timeout_title">Network Timeout</string>
<string name="settings.network_timeout_10000">10 seconds</string>
<string name="settings.network_timeout_15000">15 seconds</string>
@@ -491,6 +493,8 @@
<string name="settings.casting_cache_summary">Cache currently playing songs while casting</string>
<string name="settings.casting.dlna_casting_enabled">DLNA Enabled</string>
<string name="settings.casting.dlna_casting_enabled.summary">If you are having battery drain problems on Android 7.0 try turning this off</string>
+ <string name="settings.rewind_interval">Rewind Interval</string>
+ <string name="settings.fastforward_interval">Fast Forward Interval</string>
<string name="shuffle.title">Shuffle By</string>
<string name="shuffle.startYear">Start Year:</string>
@@ -498,6 +502,7 @@
<string name="shuffle.genre">Genre:</string>
<string name="shuffle.pick_genre">Pick a genre</string>
+ <string name="share.create_error">Failed to create share %s</string>
<string name="share.expires">Expires: %s</string>
<string name="share.expires_never">Never Expires</string>
<string name="share.deleted">Deleted share %s</string>
diff --git a/app/src/main/res/xml/settings_cache.xml b/app/src/main/res/xml/settings_cache.xml
index 248572ca..76926379 100644
--- a/app/src/main/res/xml/settings_cache.xml
+++ b/app/src/main/res/xml/settings_cache.xml
@@ -40,6 +40,12 @@
android:key="wifiRequiredForDownload"
android:defaultValue="false"/>
+ <CheckBoxPreference
+ android:title="@string/settings.local_network_required_title"
+ android:summary="@string/settings.local_network_required_summary"
+ android:key="localNetworkRequiredForDownload"
+ android:defaultValue="false"/>
+
<ListPreference
android:title="@string/settings.network_timeout_title"
android:key="networkTimeout"
@@ -96,4 +102,4 @@
android:key="screenLitOnDownload"
android:defaultValue="true"/>
</PreferenceCategory>
-</PreferenceScreen> \ No newline at end of file
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/settings_playback.xml b/app/src/main/res/xml/settings_playback.xml
index da31d071..edaf3013 100644
--- a/app/src/main/res/xml/settings_playback.xml
+++ b/app/src/main/res/xml/settings_playback.xml
@@ -52,6 +52,20 @@
android:defaultValue="all"
android:entryValues="@array/songPressActionValues"
android:entries="@array/songPressActionNames"/>
+
+ <github.daneren2005.dsub.view.SeekBarPreference
+ android:title="@string/settings.rewind_interval"
+ android:key="rewindInterval"
+ android:defaultValue="10"
+ android:dialogLayout="@layout/seekbar_preference"
+ myns:max="60"/>
+
+ <github.daneren2005.dsub.view.SeekBarPreference
+ android:title="@string/settings.fastforward_interval"
+ android:key="fastforwardInterval"
+ android:defaultValue="30"
+ android:dialogLayout="@layout/seekbar_preference"
+ myns:max="60"/>
</PreferenceCategory>
<PreferenceCategory